Skip to content

Commit

Permalink
Improve Linux HiDPI Support (#1266)
Browse files Browse the repository at this point in the history
Various HiDPI improvements
  • Loading branch information
tresf committed May 28, 2024
1 parent 875eae1 commit 3b4a9d9
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 24 deletions.
3 changes: 2 additions & 1 deletion src/qz/ui/LogDialog.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public LogDialog(JMenuItem caller, IconCache iconCache) {
}

public void initComponents() {
int defaultFontSize = new JLabel().getFont().getSize();
LinkLabel logDirLabel = new LinkLabel(FileUtilities.USER_DIR + File.separator);
logDirLabel.setLinkLocation(new File(FileUtilities.USER_DIR + File.separator));
setHeader(logDirLabel);
Expand All @@ -58,7 +59,7 @@ public void flush() {
logArea.setEditable(false);
logArea.setLineWrap(true);
logArea.setWrapStyleWord(true);
logArea.setFont(new Font("", Font.PLAIN, 12)); //force fallback font for character support
logArea.setFont(new Font("", Font.PLAIN, defaultFontSize)); //force fallback font for character support

// TODO: Fix button panel resizing issues
clearButton = addPanelButton("Clear", IconCache.Icon.DELETE_ICON, KeyEvent.VK_L);
Expand Down
17 changes: 13 additions & 4 deletions src/qz/ui/component/DisplayTable.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package qz.ui.component;

import qz.utils.SystemUtilities;

import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.awt.*;

/**
* Displays information in a JTable
Expand All @@ -28,6 +29,12 @@ private void initComponents() {
model.addColumn("Field");
model.addColumn("Value");

// Fix Linux row height
int origHeight = getRowHeight();
if(SystemUtilities.getWindowScaleFactor() != 1) {
setRowHeight((int)(origHeight * SystemUtilities.getWindowScaleFactor()));
}

getTableHeader().setReorderingAllowed(false);
setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
setRowSelectionAllowed(true);
Expand Down Expand Up @@ -55,9 +62,11 @@ public void autoSize(int rows, int columns) {
model.addRow(new Object[columns]);
}

int normalWidth = (int)getPreferredScrollableViewportSize().getWidth();
int autoHeight = (int)getPreferredSize().getHeight();
setPreferredScrollableViewportSize(new Dimension(normalWidth, autoHeight));
setPreferredScrollableViewportSize(
SystemUtilities.scaleWindowDimension(
getPreferredScrollableViewportSize().getWidth(),
getPreferredSize().getHeight())
);
setFillsViewportHeight(true);
refreshComponents();
}
Expand Down
53 changes: 49 additions & 4 deletions src/qz/ui/component/IconCache.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public enum Icon {
BANNER_ICON("qz-banner.png");

private boolean padded = false;
final String[] fileNames;
private String[] fileNames;

/**
* Default constructor
Expand Down Expand Up @@ -133,6 +133,11 @@ public String getId() {
}

public String[] getIds() { return fileNames; }

private void addId(String id) {
fileNames = Arrays.copyOf(fileNames, fileNames.length + 1);
fileNames[fileNames.length - 1] = id;
}
}

private final HashMap<String,ImageIcon> imageIcons;
Expand Down Expand Up @@ -161,6 +166,32 @@ private void buildIconCache() {
images.put(id, bi);
}
}
// Stash scaled 2x, 3x versions if missing
int maxScale = 3;
for(Icon i : Icon.values()) {
// Assume single-resource icons are lonely and want scaled instances
if (i.fileNames.length != 1) {
continue;
}
for(int scale = 2; scale <= maxScale; scale++) {
BufferedImage bi = images.get(i.getId());
// Assume square icon (filename is derived from width only)
String id = i.getId();
int loc = id.lastIndexOf(".");
if(loc == -1) {
continue;
}
String name = id.substring(0, loc);
String ext = id.substring(loc + 1);
String newSize = String.format("%s-%s.%s", name, bi.getWidth() * scale, ext);
if (!images.containsKey(newSize)) {
i.addId(newSize);
BufferedImage newBi = clone(bi, scale);
imageIcons.put(newSize, new ImageIcon(newBi));
images.put(newSize, newBi);
}
}
}
}

/**
Expand All @@ -170,10 +201,20 @@ private void buildIconCache() {
* @return the ImageIcon in the cache
*/
public ImageIcon getIcon(Icon i) {
return imageIcons.get(i.getId());
return SystemUtilities.getWindowScaleFactor() != 1 ?
getIcon(i, true) : imageIcons.get(i.getId());
}

private ImageIcon getIcon(Icon i, boolean inferScale) {
if(!inferScale) {
return imageIcons.get(i.getId());
}
ImageIcon baseIcon = imageIcons.get(i.getId());
Dimension scaled = SystemUtilities.scaleWindowDimension(baseIcon.getIconWidth(), baseIcon.getIconHeight());
return imageIcons.get(i.getId(scaled));
}

public ImageIcon getIcon(String id) {
private ImageIcon getIcon(String id) {
return imageIcons.get(id);
}

Expand Down Expand Up @@ -287,7 +328,11 @@ public void fixTrayIcons(boolean darkTaskbar) {
}

public static BufferedImage clone(BufferedImage src) {
Image tmp = src.getScaledInstance(src.getWidth(), src.getHeight(), src.getType());
return clone(src, 1);
}

public static BufferedImage clone(BufferedImage src, int scaleFactor) {
Image tmp = src.getScaledInstance(src.getWidth() * scaleFactor, src.getHeight() * scaleFactor, src.getType());
BufferedImage dest = new BufferedImage(tmp.getWidth(null), tmp.getHeight(null), BufferedImage.TYPE_INT_ARGB);
Graphics g = dest.createGraphics();
g.drawImage(tmp, 0, 0, null);
Expand Down
51 changes: 36 additions & 15 deletions src/qz/utils/SystemUtilities.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ public class SystemUtilities {
private static final Os OS_TYPE = Os.bestMatch(OS_NAME);
private static final Arch JRE_ARCH = Arch.bestMatch(OS_ARCH);
private static final Logger log = LogManager.getLogger(TrayManager.class);

private static double windowScaleFactor = -1;
private static final Locale defaultLocale = Locale.getDefault();

static {
Expand Down Expand Up @@ -473,7 +475,7 @@ public static void centerDialog(Dialog dialog, Point position) {
}

//adjust for dpi scaling
double dpiScale = getWindowScaleFactor();
double dpiScale = getWindowScaleFactor(true);
if (dpiScale == 0) {
log.debug("Invalid window scale value: {}, we'll center on the primary monitor instead", dpiScale);
dialog.setLocationRelativeTo(null);
Expand Down Expand Up @@ -517,21 +519,40 @@ public static boolean isWindowLocationValid(Rectangle window) {
* See issues #284, #448
* @return Logical dpi scale as dpi/96
*/
public static double getWindowScaleFactor() {
// MacOS is always 1
if (isMac()) {
return 1;
}
// Windows/Linux on JDK8 honors scaling
if (Constants.JAVA_VERSION.lessThan(Version.valueOf("11.0.0"))) {
return Toolkit.getDefaultToolkit().getScreenResolution() / 96.0;
}
// Windows on JDK11 is always 1
if(isWindows()) {
return 1;
private static double getWindowScaleFactor(boolean forceRefresh) {
if(windowScaleFactor == -1 || forceRefresh) {
// MacOS is always 1
if (isMac()) {
return windowScaleFactor = 1;
}
// Windows/Linux on JDK8 honors scaling
if (Constants.JAVA_VERSION.lessThan(Version.valueOf("11.0.0"))) {
return windowScaleFactor = Toolkit.getDefaultToolkit().getScreenResolution() / 96.0;
}
// Windows on JDK11 is always 1
if (isWindows()) {
return windowScaleFactor = 1;
}
// Linux/Unix on JDK11 requires JNA calls to Gdk
return windowScaleFactor = UnixUtilities.getScaleFactor();
}
// Linux/Unix on JDK11 requires JNA calls to Gdk
return UnixUtilities.getScaleFactor();
return windowScaleFactor;
}

public static double getWindowScaleFactor() {
return getWindowScaleFactor(false);
}

public static Dimension scaleWindowDimension(Dimension orig) {
return scaleWindowDimension(orig.getWidth(), orig.getHeight());
}

public static Dimension scaleWindowDimension(double width, double height) {
double scaleFactor = getWindowScaleFactor();
return new Dimension(
(int)(width * scaleFactor),
(int)(height * scaleFactor)
);
}

/**
Expand Down

0 comments on commit 3b4a9d9

Please sign in to comment.