Scale the interface fractionally on Linux

Java2D on X11 (and so under XWayland) truncates its scale to a whole
number, drawing 150 % at 100 %. On Linux, pin Java2D to 1 and hand the
full factor to FlatLaf, taken from XSETTINGS Xft/DPI, KWin's Xwayland
scale or GDK_SCALE, or chosen in Options → Design.

The client's own sizes now go through UIScale: borders, gaps, dialog and
row sizes, icons (rendered at the scaled size, so they stay crisp),
custom-painted components and font sizes in the HTML views.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-23 22:33:27 +00:00
parent 1fddae3f0b
commit 9d996f48a2
57 changed files with 643 additions and 311 deletions

View File

@@ -1,5 +1,6 @@
package com.ts3client.ui;
import com.formdev.flatlaf.util.UIScale;
import com.ts3client.audio.AudioBackend;
import com.ts3client.audio.desktop.DesktopAudioBackend;
import com.ts3client.config.AwayMessages;
@@ -14,7 +15,6 @@ import com.ts3client.net.ChannelNode;
import com.ts3client.sound.SoundNotifier;
import com.ts3client.sound.SoundPlayer;
import javax.swing.BorderFactory;
import javax.swing.JCheckBox;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFrame;
@@ -104,7 +104,7 @@ public final class MainFrame extends JFrame implements ServerTabPane.Listener {
});
// Catches Ctrl+C / SIGTERM so we still leave the servers cleanly.
Runtime.getRuntime().addShutdownHook(shutdownHook);
setMinimumSize(new Dimension(720, 480));
setMinimumSize(UIScale.scale(new Dimension(720, 480)));
tray = new TrayController(this, this::quit);
menuBar = buildMenuBar();
@@ -128,7 +128,7 @@ public final class MainFrame extends JFrame implements ServerTabPane.Listener {
statusTimer = new javax.swing.Timer(3000, e -> updateConnectionStatus());
statusTimer.start();
setSize(880, 560);
setSize(UIScale.scale(new Dimension(880, 560)));
setLocationRelativeTo(null);
// Deferred so the window is on screen before we start connecting.
@@ -639,7 +639,7 @@ public final class MainFrame extends JFrame implements ServerTabPane.Listener {
JTextField labelField = new JTextField(selected.title());
JCheckBox joinChannel = new JCheckBox("Join \"" + channelPath + "\" on connect", !channelPath.isEmpty());
JPanel form = new JPanel(new GridLayout(0, 1, 0, 2));
JPanel form = new JPanel(new GridLayout(0, 1, 0, UIScale.scale(2)));
form.add(new JLabel("Bookmark label:"));
form.add(labelField);
if (!channelPath.isEmpty()) form.add(joinChannel);