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:
@@ -169,6 +169,18 @@ frontend supplies its own UI and audio backend while reusing `core` unchanged.
|
|||||||
- **Pick one in Options → Design**, which also shows a viewer of everything the pack
|
- **Pick one in Options → Design**, which also shows a viewer of everything the pack
|
||||||
contains; switching redraws the window right away.
|
contains; switching redraws the window right away.
|
||||||
|
|
||||||
|
### Display scaling (Linux)
|
||||||
|
- Java2D on X11 (and so under XWayland) only scales by whole numbers, so 125 % or
|
||||||
|
150 % comes out at 100 %. On Linux the client pins Java2D to 1 and hands the full
|
||||||
|
factor to FlatLaf instead, scaling its own sizes and icons to match.
|
||||||
|
- The factor follows the desktop: XSETTINGS `Xft/DPI` (GNOME, Plasma), else KWin's
|
||||||
|
XWayland scale, else `GDK_SCALE`. For crisp text on Wayland, let X11 apps scale
|
||||||
|
themselves (Plasma: *Display Configuration → Legacy Applications → Apply scaling
|
||||||
|
themselves*; GNOME: the `xwayland-native-scaling` mutter feature), otherwise the
|
||||||
|
compositor upscales the window.
|
||||||
|
- **Options → Design → Interface scale** overrides it (after a restart);
|
||||||
|
`-Dflatlaf.uiScale=` or `-Dsun.java2d.uiScale=` on the command line bypasses all this.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
- Java 26+ (developed/tested on Temurin 26)
|
- Java 26+ (developed/tested on Temurin 26)
|
||||||
- The native Opus library on the system:
|
- The native Opus library on the system:
|
||||||
@@ -269,6 +281,7 @@ swing/ com.ts3client
|
|||||||
├── HotkeyActions carries a fired hotkey out on the client
|
├── HotkeyActions carries a fired hotkey out on the client
|
||||||
├── NotificationsPanel sound pack + per-action sound/important configuration
|
├── NotificationsPanel sound pack + per-action sound/important configuration
|
||||||
├── IconPackPanel icon pack chooser + icon viewer (Options → Design)
|
├── IconPackPanel icon pack chooser + icon viewer (Options → Design)
|
||||||
|
├── DisplayScale interface scale on Linux (fractional, via FlatLaf)
|
||||||
├── ConnectDialog connect form
|
├── ConnectDialog connect form
|
||||||
├── BookmarksDialog manage saved servers
|
├── BookmarksDialog manage saved servers
|
||||||
├── IdentitiesDialog manage identities (new/import/export/improve)
|
├── IdentitiesDialog manage identities (new/import/export/improve)
|
||||||
|
|||||||
@@ -154,6 +154,8 @@ public final class Settings {
|
|||||||
|
|
||||||
/** Look-and-feel variant; see {@link Appearance}. */
|
/** Look-and-feel variant; see {@link Appearance}. */
|
||||||
public Appearance appearance = Appearance.LIGHT;
|
public Appearance appearance = Appearance.LIGHT;
|
||||||
|
/** Interface scale factor, 1.0 being 100 %; 0 follows the desktop. Read at startup. */
|
||||||
|
public double uiScale = 0;
|
||||||
|
|
||||||
// ---- window chrome ----
|
// ---- window chrome ----
|
||||||
/** Whether the status bar at the bottom of the window is shown. */
|
/** Whether the status bar at the bottom of the window is shown. */
|
||||||
@@ -263,6 +265,7 @@ public final class Settings {
|
|||||||
iconPack = props.getProperty("iconPack", iconPack);
|
iconPack = props.getProperty("iconPack", iconPack);
|
||||||
iconPackDir = props.getProperty("iconPackDir", iconPackDir);
|
iconPackDir = props.getProperty("iconPackDir", iconPackDir);
|
||||||
appearance = parseAppearance(props.getProperty("appearance"), appearance);
|
appearance = parseAppearance(props.getProperty("appearance"), appearance);
|
||||||
|
uiScale = parseD(props.getProperty("uiScale"), uiScale);
|
||||||
showStatusBar = parseB(props.getProperty("showStatusBar"), showStatusBar);
|
showStatusBar = parseB(props.getProperty("showStatusBar"), showStatusBar);
|
||||||
showMasterVolumeSlider = parseB(props.getProperty("showMasterVolumeSlider"), showMasterVolumeSlider);
|
showMasterVolumeSlider = parseB(props.getProperty("showMasterVolumeSlider"), showMasterVolumeSlider);
|
||||||
infoSplitRatio = parseD(props.getProperty("infoSplitRatio"), infoSplitRatio);
|
infoSplitRatio = parseD(props.getProperty("infoSplitRatio"), infoSplitRatio);
|
||||||
@@ -311,6 +314,7 @@ public final class Settings {
|
|||||||
props.setProperty("iconPack", iconPack);
|
props.setProperty("iconPack", iconPack);
|
||||||
props.setProperty("iconPackDir", iconPackDir);
|
props.setProperty("iconPackDir", iconPackDir);
|
||||||
props.setProperty("appearance", appearance.name());
|
props.setProperty("appearance", appearance.name());
|
||||||
|
props.setProperty("uiScale", Double.toString(uiScale));
|
||||||
props.setProperty("showStatusBar", Boolean.toString(showStatusBar));
|
props.setProperty("showStatusBar", Boolean.toString(showStatusBar));
|
||||||
props.setProperty("showMasterVolumeSlider", Boolean.toString(showMasterVolumeSlider));
|
props.setProperty("showMasterVolumeSlider", Boolean.toString(showMasterVolumeSlider));
|
||||||
props.setProperty("infoSplitRatio", Double.toString(infoSplitRatio));
|
props.setProperty("infoSplitRatio", Double.toString(infoSplitRatio));
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.ts3client;
|
package com.ts3client;
|
||||||
|
|
||||||
import com.ts3client.config.Settings;
|
import com.ts3client.config.Settings;
|
||||||
|
import com.ts3client.ui.DisplayScale;
|
||||||
import com.ts3client.ui.IconTheme;
|
import com.ts3client.ui.IconTheme;
|
||||||
import com.ts3client.ui.LookAndFeelManager;
|
import com.ts3client.ui.LookAndFeelManager;
|
||||||
import com.ts3client.ui.MainFrame;
|
import com.ts3client.ui.MainFrame;
|
||||||
@@ -21,6 +22,7 @@ public final class Main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final Settings settings = Settings.load();
|
final Settings settings = Settings.load();
|
||||||
|
DisplayScale.configure(settings.uiScale);
|
||||||
|
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
LookAndFeelManager.install(settings.appearance);
|
LookAndFeelManager.install(settings.appearance);
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.ScaledEmptyBorder;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.net.ClientEntry;
|
import com.ts3client.net.ClientEntry;
|
||||||
import com.ts3client.net.TeamspeakConnection;
|
import com.ts3client.net.TeamspeakConnection;
|
||||||
import com.ts3client.net.avatar.Avatar;
|
import com.ts3client.net.avatar.Avatar;
|
||||||
@@ -39,16 +41,16 @@ final class AvatarDialog extends JDialog {
|
|||||||
|
|
||||||
JPanel picture = new JPanel(new BorderLayout());
|
JPanel picture = new JPanel(new BorderLayout());
|
||||||
picture.setBorder(BorderFactory.createCompoundBorder(
|
picture.setBorder(BorderFactory.createCompoundBorder(
|
||||||
BorderFactory.createEmptyBorder(8, 8, 4, 8),
|
new ScaledEmptyBorder(8, 8, 4, 8),
|
||||||
BorderFactory.createLineBorder(Theme.border())));
|
BorderFactory.createLineBorder(Theme.border())));
|
||||||
picture.add(preview, BorderLayout.CENTER);
|
picture.add(preview, BorderLayout.CENTER);
|
||||||
|
|
||||||
JLabel hint = new JLabel("<html>Images up to " + (conn.maxAvatarBytes() / 1024)
|
JLabel hint = new JLabel("<html>Images up to " + (conn.maxAvatarBytes() / 1024)
|
||||||
+ " KiB may be uploaded to this server. Animated GIFs stay animated.</html>");
|
+ " KiB may be uploaded to this server. Animated GIFs stay animated.</html>");
|
||||||
hint.setFont(Theme.uiSmall());
|
hint.setFont(Theme.uiSmall());
|
||||||
hint.setBorder(BorderFactory.createEmptyBorder(0, 8, 0, 8));
|
hint.setBorder(new ScaledEmptyBorder(0, 8, 0, 8));
|
||||||
status.setFont(Theme.uiSmall());
|
status.setFont(Theme.uiSmall());
|
||||||
status.setBorder(BorderFactory.createEmptyBorder(2, 8, 0, 8));
|
status.setBorder(new ScaledEmptyBorder(2, 8, 0, 8));
|
||||||
|
|
||||||
JPanel text = new JPanel();
|
JPanel text = new JPanel();
|
||||||
text.setLayout(new BoxLayout(text, BoxLayout.Y_AXIS));
|
text.setLayout(new BoxLayout(text, BoxLayout.Y_AXIS));
|
||||||
@@ -57,13 +59,13 @@ final class AvatarDialog extends JDialog {
|
|||||||
|
|
||||||
JPanel buttons = new JPanel();
|
JPanel buttons = new JPanel();
|
||||||
buttons.setLayout(new BoxLayout(buttons, BoxLayout.X_AXIS));
|
buttons.setLayout(new BoxLayout(buttons, BoxLayout.X_AXIS));
|
||||||
buttons.setBorder(BorderFactory.createEmptyBorder(6, 8, 8, 8));
|
buttons.setBorder(new ScaledEmptyBorder(6, 8, 8, 8));
|
||||||
choose.addActionListener(e -> chooseFile());
|
choose.addActionListener(e -> chooseFile());
|
||||||
remove.addActionListener(e -> removeAvatar());
|
remove.addActionListener(e -> removeAvatar());
|
||||||
JButton close = new JButton("Close");
|
JButton close = new JButton("Close");
|
||||||
close.addActionListener(e -> dispose());
|
close.addActionListener(e -> dispose());
|
||||||
buttons.add(choose);
|
buttons.add(choose);
|
||||||
buttons.add(Box.createHorizontalStrut(4));
|
buttons.add(Box.createHorizontalStrut(UIScale.scale(4)));
|
||||||
buttons.add(remove);
|
buttons.add(remove);
|
||||||
buttons.add(Box.createHorizontalGlue());
|
buttons.add(Box.createHorizontalGlue());
|
||||||
buttons.add(close);
|
buttons.add(close);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.net.avatar.Avatar;
|
import com.ts3client.net.avatar.Avatar;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
@@ -12,7 +13,8 @@ import java.util.Iterator;
|
|||||||
/**
|
/**
|
||||||
* Renders an avatar as an HTML {@code <img>} for the info views. Swing's HTML renderer
|
* Renders an avatar as an HTML {@code <img>} for the info views. Swing's HTML renderer
|
||||||
* loads images by URL and plays animated GIFs on its own, so the tag simply points at
|
* loads images by URL and plays animated GIFs on its own, so the tag simply points at
|
||||||
* the repository's on-disk copy, scaled to fit a box while keeping its aspect ratio.
|
* the repository's on-disk copy, drawn at the interface scale and fitted to a box while
|
||||||
|
* keeping its aspect ratio.
|
||||||
*/
|
*/
|
||||||
final class AvatarHtml {
|
final class AvatarHtml {
|
||||||
|
|
||||||
@@ -25,7 +27,8 @@ final class AvatarHtml {
|
|||||||
/** @return the tag, or an empty string if the avatar cannot be shown */
|
/** @return the tag, or an empty string if the avatar cannot be shown */
|
||||||
static String imgTag(Avatar avatar, int maxSize) {
|
static String imgTag(Avatar avatar, int maxSize) {
|
||||||
if (avatar == null || avatar.file() == null) return "";
|
if (avatar == null || avatar.file() == null) return "";
|
||||||
Dimension size = fit(dimensions(avatar.data()), maxSize);
|
Dimension natural = dimensions(avatar.data());
|
||||||
|
Dimension size = fit(natural == null ? null : UIScale.scale(natural), maxSize);
|
||||||
String dims = size == null ? "" : " width='" + size.width + "' height='" + size.height + "'";
|
String dims = size == null ? "" : " width='" + size.width + "' height='" + size.height + "'";
|
||||||
return "<img src='" + avatar.file().toURI() + "'" + dims + ">";
|
return "<img src='" + avatar.file().toURI() + "'" + dims + ">";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
@@ -22,7 +24,7 @@ final class AvatarView extends JComponent {
|
|||||||
|
|
||||||
AvatarView(int boxSize) {
|
AvatarView(int boxSize) {
|
||||||
this.boxSize = boxSize;
|
this.boxSize = boxSize;
|
||||||
setPreferredSize(new Dimension(boxSize, boxSize));
|
setPreferredSize(UIScale.scale(new Dimension(boxSize, boxSize)));
|
||||||
setMinimumSize(getPreferredSize());
|
setMinimumSize(getPreferredSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,7 +49,7 @@ final class AvatarView extends JComponent {
|
|||||||
imageHeight = image.getHeight(this);
|
imageHeight = image.getHeight(this);
|
||||||
if (imageWidth < 0 || imageHeight < 0) return;
|
if (imageWidth < 0 || imageHeight < 0) return;
|
||||||
}
|
}
|
||||||
double scale = Math.min(1.0, Math.min((double) getWidth() / imageWidth, (double) getHeight() / imageHeight));
|
double scale = Math.min(UIScale.getUserScaleFactor(), Math.min((double) getWidth() / imageWidth, (double) getHeight() / imageHeight));
|
||||||
int w = Math.max(1, (int) Math.round(imageWidth * scale));
|
int w = Math.max(1, (int) Math.round(imageWidth * scale));
|
||||||
int h = Math.max(1, (int) Math.round(imageHeight * scale));
|
int h = Math.max(1, (int) Math.round(imageHeight * scale));
|
||||||
Graphics2D g2 = (Graphics2D) g.create();
|
Graphics2D g2 = (Graphics2D) g.create();
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.ScaledEmptyBorder;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.config.AwayMessages;
|
import com.ts3client.config.AwayMessages;
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
|
||||||
import javax.swing.Box;
|
import javax.swing.Box;
|
||||||
import javax.swing.BoxLayout;
|
import javax.swing.BoxLayout;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
@@ -39,13 +40,13 @@ public final class AwayMessagesDialog extends JDialog {
|
|||||||
table.setTableHeader(null);
|
table.setTableHeader(null);
|
||||||
table.setShowGrid(false);
|
table.setShowGrid(false);
|
||||||
table.setFillsViewportHeight(true);
|
table.setFillsViewportHeight(true);
|
||||||
table.setRowHeight(Math.max(20, table.getRowHeight()));
|
table.setRowHeight(Math.max(UIScale.scale(20), table.getRowHeight()));
|
||||||
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||||
// Single click selects, double click starts editing — like a renameable list.
|
// Single click selects, double click starts editing — like a renameable list.
|
||||||
table.putClientProperty("JTable.autoStartsEdit", Boolean.FALSE);
|
table.putClientProperty("JTable.autoStartsEdit", Boolean.FALSE);
|
||||||
|
|
||||||
JScrollPane scroll = new JScrollPane(table);
|
JScrollPane scroll = new JScrollPane(table);
|
||||||
scroll.setBorder(BorderFactory.createEmptyBorder(8, 8, 4, 8));
|
scroll.setBorder(new ScaledEmptyBorder(8, 8, 4, 8));
|
||||||
|
|
||||||
getContentPane().setLayout(new BorderLayout());
|
getContentPane().setLayout(new BorderLayout());
|
||||||
getContentPane().add(scroll, BorderLayout.CENTER);
|
getContentPane().add(scroll, BorderLayout.CENTER);
|
||||||
@@ -53,14 +54,14 @@ public final class AwayMessagesDialog extends JDialog {
|
|||||||
|
|
||||||
Dialogs.closeOnEscape(this, this::closeDialog);
|
Dialogs.closeOnEscape(this, this::closeDialog);
|
||||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||||
setSize(new Dimension(360, 300));
|
setSize(UIScale.scale(new Dimension(360, 300)));
|
||||||
setLocationRelativeTo(owner);
|
setLocationRelativeTo(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
private JPanel buildButtons() {
|
private JPanel buildButtons() {
|
||||||
JPanel buttons = new JPanel();
|
JPanel buttons = new JPanel();
|
||||||
buttons.setLayout(new BoxLayout(buttons, BoxLayout.X_AXIS));
|
buttons.setLayout(new BoxLayout(buttons, BoxLayout.X_AXIS));
|
||||||
buttons.setBorder(BorderFactory.createEmptyBorder(0, 8, 8, 8));
|
buttons.setBorder(new ScaledEmptyBorder(0, 8, 8, 8));
|
||||||
addButton(buttons, "Add", this::addEntry);
|
addButton(buttons, "Add", this::addEntry);
|
||||||
addButton(buttons, "Remove", this::removeSelected);
|
addButton(buttons, "Remove", this::removeSelected);
|
||||||
buttons.add(Box.createHorizontalGlue());
|
buttons.add(Box.createHorizontalGlue());
|
||||||
@@ -72,7 +73,7 @@ public final class AwayMessagesDialog extends JDialog {
|
|||||||
JButton b = new JButton(text);
|
JButton b = new JButton(text);
|
||||||
b.addActionListener(e -> action.run());
|
b.addActionListener(e -> action.run());
|
||||||
panel.add(b);
|
panel.add(b);
|
||||||
panel.add(Box.createHorizontalStrut(4));
|
panel.add(Box.createHorizontalStrut(UIScale.scale(4)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addEntry() {
|
private void addEntry() {
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.ScaledEmptyBorder;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.config.BanReasonPresets;
|
import com.ts3client.config.BanReasonPresets;
|
||||||
import com.ts3client.net.ban.BanEntry;
|
import com.ts3client.net.ban.BanEntry;
|
||||||
import com.ts3client.net.ban.BanPattern;
|
import com.ts3client.net.ban.BanPattern;
|
||||||
import com.ts3client.net.ban.BanRule;
|
import com.ts3client.net.ban.BanRule;
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
|
||||||
import javax.swing.Box;
|
import javax.swing.Box;
|
||||||
import javax.swing.BoxLayout;
|
import javax.swing.BoxLayout;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
@@ -75,24 +76,24 @@ final class BanAddDialog extends JDialog {
|
|||||||
|
|
||||||
JPanel form = new JPanel();
|
JPanel form = new JPanel();
|
||||||
form.setLayout(new BoxLayout(form, BoxLayout.Y_AXIS));
|
form.setLayout(new BoxLayout(form, BoxLayout.Y_AXIS));
|
||||||
form.setBorder(BorderFactory.createEmptyBorder(12, 12, 8, 12));
|
form.setBorder(new ScaledEmptyBorder(12, 12, 8, 12));
|
||||||
form.add(ReasonDialog.label("IP:"));
|
form.add(ReasonDialog.label("IP:"));
|
||||||
form.add(ipField);
|
form.add(ipField);
|
||||||
form.add(Box.createVerticalStrut(8));
|
form.add(Box.createVerticalStrut(UIScale.scale(8)));
|
||||||
form.add(ReasonDialog.label("Name:"));
|
form.add(ReasonDialog.label("Name:"));
|
||||||
form.add(nameField);
|
form.add(nameField);
|
||||||
form.add(Box.createVerticalStrut(8));
|
form.add(Box.createVerticalStrut(UIScale.scale(8)));
|
||||||
form.add(patternRow());
|
form.add(patternRow());
|
||||||
form.add(Box.createVerticalStrut(8));
|
form.add(Box.createVerticalStrut(UIScale.scale(8)));
|
||||||
form.add(ReasonDialog.label("Unique ID:"));
|
form.add(ReasonDialog.label("Unique ID:"));
|
||||||
form.add(uidField);
|
form.add(uidField);
|
||||||
form.add(Box.createVerticalStrut(8));
|
form.add(Box.createVerticalStrut(UIScale.scale(8)));
|
||||||
form.add(ReasonDialog.label("myTeamSpeak ID:"));
|
form.add(ReasonDialog.label("myTeamSpeak ID:"));
|
||||||
form.add(mytsidField);
|
form.add(mytsidField);
|
||||||
form.add(Box.createVerticalStrut(12));
|
form.add(Box.createVerticalStrut(UIScale.scale(12)));
|
||||||
form.add(reasonRow());
|
form.add(reasonRow());
|
||||||
form.add(reasonField);
|
form.add(reasonField);
|
||||||
form.add(Box.createVerticalStrut(8));
|
form.add(Box.createVerticalStrut(UIScale.scale(8)));
|
||||||
form.add(ReasonDialog.label("Duration:"));
|
form.add(ReasonDialog.label("Duration:"));
|
||||||
form.add(duration);
|
form.add(duration);
|
||||||
|
|
||||||
@@ -119,7 +120,7 @@ final class BanAddDialog extends JDialog {
|
|||||||
row.setLayout(new BoxLayout(row, BoxLayout.X_AXIS));
|
row.setLayout(new BoxLayout(row, BoxLayout.X_AXIS));
|
||||||
row.setAlignmentX(Component.LEFT_ALIGNMENT);
|
row.setAlignmentX(Component.LEFT_ALIGNMENT);
|
||||||
row.add(new JLabel("Interpret IP/Name as:"));
|
row.add(new JLabel("Interpret IP/Name as:"));
|
||||||
row.add(Box.createHorizontalStrut(8));
|
row.add(Box.createHorizontalStrut(UIScale.scale(8)));
|
||||||
row.add(patternBox);
|
row.add(patternBox);
|
||||||
row.add(Box.createHorizontalGlue());
|
row.add(Box.createHorizontalGlue());
|
||||||
ReasonDialog.fixHeight(row);
|
ReasonDialog.fixHeight(row);
|
||||||
@@ -137,13 +138,13 @@ final class BanAddDialog extends JDialog {
|
|||||||
JPanel row = new JPanel();
|
JPanel row = new JPanel();
|
||||||
row.setLayout(new BoxLayout(row, BoxLayout.X_AXIS));
|
row.setLayout(new BoxLayout(row, BoxLayout.X_AXIS));
|
||||||
row.setAlignmentX(Component.LEFT_ALIGNMENT);
|
row.setAlignmentX(Component.LEFT_ALIGNMENT);
|
||||||
row.setBorder(BorderFactory.createEmptyBorder(0, 0, 4, 0));
|
row.setBorder(new ScaledEmptyBorder(0, 0, 4, 0));
|
||||||
row.add(new JLabel("Reason:"));
|
row.add(new JLabel("Reason:"));
|
||||||
row.add(Box.createHorizontalGlue());
|
row.add(Box.createHorizontalGlue());
|
||||||
row.add(new JLabel("Preset:"));
|
row.add(new JLabel("Preset:"));
|
||||||
row.add(Box.createHorizontalStrut(6));
|
row.add(Box.createHorizontalStrut(UIScale.scale(6)));
|
||||||
row.add(presetBox);
|
row.add(presetBox);
|
||||||
row.add(Box.createHorizontalStrut(6));
|
row.add(Box.createHorizontalStrut(UIScale.scale(6)));
|
||||||
row.add(save);
|
row.add(save);
|
||||||
ReasonDialog.fixHeight(row);
|
ReasonDialog.fixHeight(row);
|
||||||
return row;
|
return row;
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.ScaledEmptyBorder;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.net.ban.BanRule;
|
import com.ts3client.net.ban.BanRule;
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
|
||||||
import javax.swing.Box;
|
import javax.swing.Box;
|
||||||
import javax.swing.BoxLayout;
|
import javax.swing.BoxLayout;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
@@ -32,12 +33,12 @@ final class BanDialog extends JDialog {
|
|||||||
|
|
||||||
JPanel form = new JPanel();
|
JPanel form = new JPanel();
|
||||||
form.setLayout(new BoxLayout(form, BoxLayout.Y_AXIS));
|
form.setLayout(new BoxLayout(form, BoxLayout.Y_AXIS));
|
||||||
form.setBorder(BorderFactory.createEmptyBorder(12, 12, 8, 12));
|
form.setBorder(new ScaledEmptyBorder(12, 12, 8, 12));
|
||||||
form.add(ReasonDialog.label("Ban " + nickname + " from the server."));
|
form.add(ReasonDialog.label("Ban " + nickname + " from the server."));
|
||||||
form.add(Box.createVerticalStrut(8));
|
form.add(Box.createVerticalStrut(UIScale.scale(8)));
|
||||||
form.add(ReasonDialog.label("Reason:"));
|
form.add(ReasonDialog.label("Reason:"));
|
||||||
form.add(reasonField);
|
form.add(reasonField);
|
||||||
form.add(Box.createVerticalStrut(8));
|
form.add(Box.createVerticalStrut(UIScale.scale(8)));
|
||||||
form.add(ReasonDialog.label("Duration:"));
|
form.add(ReasonDialog.label("Duration:"));
|
||||||
form.add(duration);
|
form.add(duration);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
import javax.swing.Box;
|
import javax.swing.Box;
|
||||||
import javax.swing.BoxLayout;
|
import javax.swing.BoxLayout;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
@@ -42,9 +44,9 @@ final class BanDurationPicker extends JPanel {
|
|||||||
max.addActionListener(e -> setSeconds(maxSeconds));
|
max.addActionListener(e -> setSeconds(maxSeconds));
|
||||||
|
|
||||||
add(amount);
|
add(amount);
|
||||||
add(Box.createHorizontalStrut(4));
|
add(Box.createHorizontalStrut(UIScale.scale(4)));
|
||||||
add(unit);
|
add(unit);
|
||||||
add(Box.createHorizontalStrut(4));
|
add(Box.createHorizontalStrut(UIScale.scale(4)));
|
||||||
add(max);
|
add(max);
|
||||||
add(Box.createHorizontalGlue());
|
add(Box.createHorizontalGlue());
|
||||||
ReasonDialog.fixHeight(this);
|
ReasonDialog.fixHeight(this);
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.ScaledEmptyBorder;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.config.BanReasonPresets;
|
import com.ts3client.config.BanReasonPresets;
|
||||||
import com.ts3client.config.Settings;
|
import com.ts3client.config.Settings;
|
||||||
import com.ts3client.net.ServerModel;
|
import com.ts3client.net.ServerModel;
|
||||||
@@ -8,7 +10,6 @@ import com.ts3client.net.ban.BanEntry;
|
|||||||
import com.ts3client.net.ban.BanRule;
|
import com.ts3client.net.ban.BanRule;
|
||||||
|
|
||||||
import javax.swing.AbstractAction;
|
import javax.swing.AbstractAction;
|
||||||
import javax.swing.BorderFactory;
|
|
||||||
import javax.swing.Box;
|
import javax.swing.Box;
|
||||||
import javax.swing.BoxLayout;
|
import javax.swing.BoxLayout;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
@@ -122,9 +123,9 @@ final class BanListDialog extends JDialog {
|
|||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
this.presets = presets;
|
this.presets = presets;
|
||||||
|
|
||||||
JPanel content = new JPanel(new BorderLayout(0, 6));
|
JPanel content = new JPanel(new BorderLayout(0, UIScale.scale(6)));
|
||||||
content.setBackground(Theme.windowBg());
|
content.setBackground(Theme.windowBg());
|
||||||
content.setBorder(BorderFactory.createEmptyBorder(10, 12, 10, 12));
|
content.setBorder(new ScaledEmptyBorder(10, 12, 10, 12));
|
||||||
content.add(buildToolbar(), BorderLayout.NORTH);
|
content.add(buildToolbar(), BorderLayout.NORTH);
|
||||||
content.add(buildTable(), BorderLayout.CENTER);
|
content.add(buildTable(), BorderLayout.CENTER);
|
||||||
content.add(buildBottom(), BorderLayout.SOUTH);
|
content.add(buildBottom(), BorderLayout.SOUTH);
|
||||||
@@ -133,7 +134,7 @@ final class BanListDialog extends JDialog {
|
|||||||
|
|
||||||
serverNameChanged();
|
serverNameChanged();
|
||||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||||
setPreferredSize(new Dimension(920, 460));
|
setPreferredSize(UIScale.scale(new Dimension(920, 460)));
|
||||||
pack();
|
pack();
|
||||||
setLocationRelativeTo(owner);
|
setLocationRelativeTo(owner);
|
||||||
reload();
|
reload();
|
||||||
@@ -172,18 +173,18 @@ final class BanListDialog extends JDialog {
|
|||||||
searchField.setMaximumSize(new Dimension(Integer.MAX_VALUE, searchField.getPreferredSize().height));
|
searchField.setMaximumSize(new Dimension(Integer.MAX_VALUE, searchField.getPreferredSize().height));
|
||||||
|
|
||||||
bar.add(addButton);
|
bar.add(addButton);
|
||||||
bar.add(Box.createHorizontalStrut(6));
|
bar.add(Box.createHorizontalStrut(UIScale.scale(6)));
|
||||||
bar.add(removeButton);
|
bar.add(removeButton);
|
||||||
bar.add(Box.createHorizontalStrut(6));
|
bar.add(Box.createHorizontalStrut(UIScale.scale(6)));
|
||||||
bar.add(editButton);
|
bar.add(editButton);
|
||||||
bar.add(Box.createHorizontalStrut(16));
|
bar.add(Box.createHorizontalStrut(UIScale.scale(16)));
|
||||||
bar.add(searchField);
|
bar.add(searchField);
|
||||||
return bar;
|
return bar;
|
||||||
}
|
}
|
||||||
|
|
||||||
private JScrollPane buildTable() {
|
private JScrollPane buildTable() {
|
||||||
table.setFont(Theme.uiFont());
|
table.setFont(Theme.uiFont());
|
||||||
table.setRowHeight(20);
|
table.setRowHeight(UIScale.scale(20));
|
||||||
table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
||||||
table.setFillsViewportHeight(true);
|
table.setFillsViewportHeight(true);
|
||||||
table.setAutoCreateColumnsFromModel(false);
|
table.setAutoCreateColumnsFromModel(false);
|
||||||
@@ -248,9 +249,9 @@ final class BanListDialog extends JDialog {
|
|||||||
options.setLayout(new BoxLayout(options, BoxLayout.X_AXIS));
|
options.setLayout(new BoxLayout(options, BoxLayout.X_AXIS));
|
||||||
options.setOpaque(false);
|
options.setOpaque(false);
|
||||||
options.add(reload);
|
options.add(reload);
|
||||||
options.add(Box.createHorizontalStrut(12));
|
options.add(Box.createHorizontalStrut(UIScale.scale(12)));
|
||||||
options.add(onlyOwn);
|
options.add(onlyOwn);
|
||||||
options.add(Box.createHorizontalStrut(12));
|
options.add(Box.createHorizontalStrut(UIScale.scale(12)));
|
||||||
options.add(highlightOwn);
|
options.add(highlightOwn);
|
||||||
options.add(Box.createHorizontalGlue());
|
options.add(Box.createHorizontalGlue());
|
||||||
|
|
||||||
@@ -260,7 +261,7 @@ final class BanListDialog extends JDialog {
|
|||||||
closeRow.setOpaque(false);
|
closeRow.setOpaque(false);
|
||||||
closeRow.add(close, BorderLayout.EAST);
|
closeRow.add(close, BorderLayout.EAST);
|
||||||
|
|
||||||
JPanel bottom = new JPanel(new BorderLayout(0, 8));
|
JPanel bottom = new JPanel(new BorderLayout(0, UIScale.scale(8)));
|
||||||
bottom.setOpaque(false);
|
bottom.setOpaque(false);
|
||||||
bottom.add(status, BorderLayout.NORTH);
|
bottom.add(status, BorderLayout.NORTH);
|
||||||
bottom.add(options, BorderLayout.CENTER);
|
bottom.add(options, BorderLayout.CENTER);
|
||||||
@@ -659,7 +660,7 @@ final class BanListDialog extends JDialog {
|
|||||||
g2.setColor(Theme.chatEvent());
|
g2.setColor(Theme.chatEvent());
|
||||||
g2.setFont(getFont().deriveFont(Font.ITALIC));
|
g2.setFont(getFont().deriveFont(Font.ITALIC));
|
||||||
int y = (getHeight() + g2.getFontMetrics().getAscent() - g2.getFontMetrics().getDescent()) / 2;
|
int y = (getHeight() + g2.getFontMetrics().getAscent() - g2.getFontMetrics().getDescent()) / 2;
|
||||||
g2.drawString("Search", getInsets().left + 2, y);
|
g2.drawString("Search", getInsets().left + UIScale.scale(2), y);
|
||||||
g2.dispose();
|
g2.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.ScaledEmptyBorder;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.config.Bookmark;
|
import com.ts3client.config.Bookmark;
|
||||||
import com.ts3client.config.Bookmarks;
|
import com.ts3client.config.Bookmarks;
|
||||||
import com.ts3client.config.IdentityStore;
|
import com.ts3client.config.IdentityStore;
|
||||||
@@ -7,7 +9,6 @@ import com.ts3client.config.Settings;
|
|||||||
import com.ts3client.teamspeak.TeamSpeakImporter;
|
import com.ts3client.teamspeak.TeamSpeakImporter;
|
||||||
import com.ts3client.teamspeak.TeamSpeakSettingsDb;
|
import com.ts3client.teamspeak.TeamSpeakSettingsDb;
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
|
||||||
import javax.swing.Box;
|
import javax.swing.Box;
|
||||||
import javax.swing.BoxLayout;
|
import javax.swing.BoxLayout;
|
||||||
import javax.swing.DefaultListModel;
|
import javax.swing.DefaultListModel;
|
||||||
@@ -81,10 +82,10 @@ public final class BookmarksDialog extends JDialog {
|
|||||||
details = buildDetails();
|
details = buildDetails();
|
||||||
|
|
||||||
JScrollPane listScroll = new JScrollPane(list);
|
JScrollPane listScroll = new JScrollPane(list);
|
||||||
listScroll.setPreferredSize(new Dimension(180, 320));
|
listScroll.setPreferredSize(UIScale.scale(new Dimension(180, 320)));
|
||||||
|
|
||||||
JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, listScroll, details);
|
JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, listScroll, details);
|
||||||
split.setBorder(BorderFactory.createEmptyBorder(8, 8, 4, 8));
|
split.setBorder(new ScaledEmptyBorder(8, 8, 4, 8));
|
||||||
split.setResizeWeight(0.3);
|
split.setResizeWeight(0.3);
|
||||||
split.setContinuousLayout(true);
|
split.setContinuousLayout(true);
|
||||||
|
|
||||||
@@ -105,7 +106,7 @@ public final class BookmarksDialog extends JDialog {
|
|||||||
persist();
|
persist();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
setSize(new Dimension(640, 400));
|
setSize(UIScale.scale(new Dimension(640, 400)));
|
||||||
setLocationRelativeTo(owner);
|
setLocationRelativeTo(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,7 +114,7 @@ public final class BookmarksDialog extends JDialog {
|
|||||||
|
|
||||||
private JPanel buildDetails() {
|
private JPanel buildDetails() {
|
||||||
JPanel panel = new JPanel(new GridBagLayout());
|
JPanel panel = new JPanel(new GridBagLayout());
|
||||||
panel.setBorder(BorderFactory.createEmptyBorder(0, 8, 0, 0));
|
panel.setBorder(new ScaledEmptyBorder(0, 8, 0, 0));
|
||||||
|
|
||||||
channel.setToolTipText("Channel path, e.g. \"Lobby/Games\", or a channel id. "
|
channel.setToolTipText("Channel path, e.g. \"Lobby/Games\", or a channel id. "
|
||||||
+ "Leave empty for the server's default channel.");
|
+ "Leave empty for the server's default channel.");
|
||||||
@@ -143,7 +144,7 @@ public final class BookmarksDialog extends JDialog {
|
|||||||
private void addRow(JPanel panel, int row, String text, java.awt.Component field) {
|
private void addRow(JPanel panel, int row, String text, java.awt.Component field) {
|
||||||
GridBagConstraints c = new GridBagConstraints();
|
GridBagConstraints c = new GridBagConstraints();
|
||||||
c.gridy = row;
|
c.gridy = row;
|
||||||
c.insets = new Insets(2, 0, 2, 4);
|
c.insets = UIScale.scale(new Insets(2, 0, 2, 4));
|
||||||
c.anchor = GridBagConstraints.WEST;
|
c.anchor = GridBagConstraints.WEST;
|
||||||
c.gridx = 0;
|
c.gridx = 0;
|
||||||
panel.add(new JLabel(text), c);
|
panel.add(new JLabel(text), c);
|
||||||
@@ -156,7 +157,7 @@ public final class BookmarksDialog extends JDialog {
|
|||||||
private JPanel buildButtons() {
|
private JPanel buildButtons() {
|
||||||
JPanel buttons = new JPanel();
|
JPanel buttons = new JPanel();
|
||||||
buttons.setLayout(new BoxLayout(buttons, BoxLayout.X_AXIS));
|
buttons.setLayout(new BoxLayout(buttons, BoxLayout.X_AXIS));
|
||||||
buttons.setBorder(BorderFactory.createEmptyBorder(0, 8, 8, 8));
|
buttons.setBorder(new ScaledEmptyBorder(0, 8, 8, 8));
|
||||||
addButton(buttons, "Add", this::addBookmark);
|
addButton(buttons, "Add", this::addBookmark);
|
||||||
addButton(buttons, "Remove", this::removeSelected);
|
addButton(buttons, "Remove", this::removeSelected);
|
||||||
if (TeamSpeakSettingsDb.locate() != null) {
|
if (TeamSpeakSettingsDb.locate() != null) {
|
||||||
@@ -174,7 +175,7 @@ public final class BookmarksDialog extends JDialog {
|
|||||||
JButton b = new JButton(text);
|
JButton b = new JButton(text);
|
||||||
b.addActionListener(e -> action.run());
|
b.addActionListener(e -> action.run());
|
||||||
panel.add(b);
|
panel.add(b);
|
||||||
panel.add(Box.createHorizontalStrut(4));
|
panel.add(Box.createHorizontalStrut(UIScale.scale(4)));
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.ScaledEmptyBorder;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.net.ChannelSettings;
|
import com.ts3client.net.ChannelSettings;
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
import javax.swing.BorderFactory;
|
||||||
@@ -47,7 +49,7 @@ final class ChannelAdvancedPanel extends JPanel implements ChannelDialog.Tab {
|
|||||||
|
|
||||||
ChannelAdvancedPanel() {
|
ChannelAdvancedPanel() {
|
||||||
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
|
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
|
||||||
setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
|
setBorder(new ScaledEmptyBorder(8, 8, 8, 8));
|
||||||
|
|
||||||
phoneticName.setToolTipText("Set phonetic nickname used for this channel by text-to-speech.");
|
phoneticName.setToolTipText("Set phonetic nickname used for this channel by text-to-speech.");
|
||||||
deleteDelay.setToolTipText("<html><p>Delay in seconds after temporary channels are deleted"
|
deleteDelay.setToolTipText("<html><p>Delay in seconds after temporary channels are deleted"
|
||||||
@@ -79,12 +81,12 @@ final class ChannelAdvancedPanel extends JPanel implements ChannelDialog.Tab {
|
|||||||
other.setAlignmentX(0f);
|
other.setAlignmentX(0f);
|
||||||
fixHeight(other);
|
fixHeight(other);
|
||||||
add(other);
|
add(other);
|
||||||
add(Box.createVerticalStrut(6));
|
add(Box.createVerticalStrut(UIScale.scale(6)));
|
||||||
|
|
||||||
JPanel limits = new JPanel();
|
JPanel limits = new JPanel();
|
||||||
limits.setLayout(new BoxLayout(limits, BoxLayout.X_AXIS));
|
limits.setLayout(new BoxLayout(limits, BoxLayout.X_AXIS));
|
||||||
limits.add(limitBox("Max Users", new JRadioButton[]{maxUsersUnlimited, maxUsersLimited}, maxUsers));
|
limits.add(limitBox("Max Users", new JRadioButton[]{maxUsersUnlimited, maxUsersLimited}, maxUsers));
|
||||||
limits.add(Box.createHorizontalStrut(8));
|
limits.add(Box.createHorizontalStrut(UIScale.scale(8)));
|
||||||
limits.add(limitBox("Family Max Users",
|
limits.add(limitBox("Family Max Users",
|
||||||
new JRadioButton[]{familyInherited, familyUnlimited, familyLimited}, familyMaxUsers));
|
new JRadioButton[]{familyInherited, familyUnlimited, familyLimited}, familyMaxUsers));
|
||||||
limits.setAlignmentX(0f);
|
limits.setAlignmentX(0f);
|
||||||
@@ -140,7 +142,7 @@ final class ChannelAdvancedPanel extends JPanel implements ChannelDialog.Tab {
|
|||||||
JPanel panel = new JPanel(new GridBagLayout());
|
JPanel panel = new JPanel(new GridBagLayout());
|
||||||
panel.setBorder(BorderFactory.createTitledBorder("Other Settings"));
|
panel.setBorder(BorderFactory.createTitledBorder("Other Settings"));
|
||||||
GridBagConstraints c = new GridBagConstraints();
|
GridBagConstraints c = new GridBagConstraints();
|
||||||
c.insets = new Insets(3, 4, 3, 4);
|
c.insets = UIScale.scale(new Insets(3, 4, 3, 4));
|
||||||
c.anchor = GridBagConstraints.WEST;
|
c.anchor = GridBagConstraints.WEST;
|
||||||
c.fill = GridBagConstraints.HORIZONTAL;
|
c.fill = GridBagConstraints.HORIZONTAL;
|
||||||
|
|
||||||
@@ -155,7 +157,7 @@ final class ChannelAdvancedPanel extends JPanel implements ChannelDialog.Tab {
|
|||||||
c.gridy = 1;
|
c.gridy = 1;
|
||||||
c.weightx = 0;
|
c.weightx = 0;
|
||||||
panel.add(new JLabel("Delete delay:"), c);
|
panel.add(new JLabel("Delete delay:"), c);
|
||||||
JPanel delay = new JPanel(new FlowLayout(FlowLayout.LEFT, 4, 0));
|
JPanel delay = new JPanel(new FlowLayout(FlowLayout.LEFT, UIScale.scale(4), 0));
|
||||||
delay.add(deleteDelay);
|
delay.add(deleteDelay);
|
||||||
delay.add(deleteDelayMax);
|
delay.add(deleteDelayMax);
|
||||||
c.gridx = 1;
|
c.gridx = 1;
|
||||||
@@ -183,7 +185,7 @@ final class ChannelAdvancedPanel extends JPanel implements ChannelDialog.Tab {
|
|||||||
box.add(radio);
|
box.add(radio);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
JPanel row = new JPanel(new FlowLayout(FlowLayout.LEFT, 6, 0));
|
JPanel row = new JPanel(new FlowLayout(FlowLayout.LEFT, UIScale.scale(6), 0));
|
||||||
row.setAlignmentX(0f);
|
row.setAlignmentX(0f);
|
||||||
row.add(radio);
|
row.add(radio);
|
||||||
row.add(spinner);
|
row.add(spinner);
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.ScaledEmptyBorder;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.net.ChannelSettings;
|
import com.ts3client.net.ChannelSettings;
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
import javax.swing.BorderFactory;
|
||||||
@@ -49,8 +51,8 @@ final class ChannelAudioPanel extends JPanel implements ChannelDialog.Tab {
|
|||||||
private final JPanel customSettings = new JPanel(new GridBagLayout());
|
private final JPanel customSettings = new JPanel(new GridBagLayout());
|
||||||
|
|
||||||
ChannelAudioPanel() {
|
ChannelAudioPanel() {
|
||||||
super(new BorderLayout(8, 8));
|
super(new BorderLayout(UIScale.scale(8), UIScale.scale(8)));
|
||||||
setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
|
setBorder(new ScaledEmptyBorder(8, 8, 8, 8));
|
||||||
|
|
||||||
ButtonGroup presets = new ButtonGroup();
|
ButtonGroup presets = new ButtonGroup();
|
||||||
presets.add(voiceMobile);
|
presets.add(voiceMobile);
|
||||||
@@ -126,7 +128,7 @@ final class ChannelAudioPanel extends JPanel implements ChannelDialog.Tab {
|
|||||||
private JPanel customBox() {
|
private JPanel customBox() {
|
||||||
customSettings.setBorder(BorderFactory.createTitledBorder("Custom Settings"));
|
customSettings.setBorder(BorderFactory.createTitledBorder("Custom Settings"));
|
||||||
GridBagConstraints c = new GridBagConstraints();
|
GridBagConstraints c = new GridBagConstraints();
|
||||||
c.insets = new Insets(4, 4, 4, 4);
|
c.insets = UIScale.scale(new Insets(4, 4, 4, 4));
|
||||||
c.anchor = GridBagConstraints.WEST;
|
c.anchor = GridBagConstraints.WEST;
|
||||||
c.fill = GridBagConstraints.HORIZONTAL;
|
c.fill = GridBagConstraints.HORIZONTAL;
|
||||||
|
|
||||||
@@ -158,7 +160,7 @@ final class ChannelAudioPanel extends JPanel implements ChannelDialog.Tab {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private JPanel bandwidthRow() {
|
private JPanel bandwidthRow() {
|
||||||
JPanel row = new JPanel(new BorderLayout(6, 0));
|
JPanel row = new JPanel(new BorderLayout(UIScale.scale(6), 0));
|
||||||
row.add(new JLabel("Bandwidth usage:"), BorderLayout.WEST);
|
row.add(new JLabel("Bandwidth usage:"), BorderLayout.WEST);
|
||||||
row.add(bandwidth, BorderLayout.CENTER);
|
row.add(bandwidth, BorderLayout.CENTER);
|
||||||
return row;
|
return row;
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.ScaledEmptyBorder;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.net.ChannelNode;
|
import com.ts3client.net.ChannelNode;
|
||||||
import com.ts3client.net.ChannelSettings;
|
import com.ts3client.net.ChannelSettings;
|
||||||
import com.ts3client.net.TeamspeakConnection;
|
import com.ts3client.net.TeamspeakConnection;
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JDialog;
|
import javax.swing.JDialog;
|
||||||
@@ -144,19 +145,19 @@ final class ChannelDialog extends JDialog {
|
|||||||
|
|
||||||
// The tabs need no more than their natural height, so they sit at the bottom
|
// The tabs need no more than their natural height, so they sit at the bottom
|
||||||
// with the buttons and every spare pixel goes to the description instead.
|
// with the buttons and every spare pixel goes to the description instead.
|
||||||
JPanel bottom = new JPanel(new BorderLayout(0, 6));
|
JPanel bottom = new JPanel(new BorderLayout(0, UIScale.scale(6)));
|
||||||
bottom.add(tabs, BorderLayout.CENTER);
|
bottom.add(tabs, BorderLayout.CENTER);
|
||||||
bottom.add(buttons(), BorderLayout.SOUTH);
|
bottom.add(buttons(), BorderLayout.SOUTH);
|
||||||
|
|
||||||
getContentPane().setLayout(new BorderLayout(0, 6));
|
getContentPane().setLayout(new BorderLayout(0, UIScale.scale(6)));
|
||||||
getContentPane().add(header(), BorderLayout.CENTER);
|
getContentPane().add(header(), BorderLayout.CENTER);
|
||||||
getContentPane().add(bottom, BorderLayout.SOUTH);
|
getContentPane().add(bottom, BorderLayout.SOUTH);
|
||||||
|
|
||||||
setEnabledForLoading(false);
|
setEnabledForLoading(false);
|
||||||
Dialogs.closeOnEscape(this);
|
Dialogs.closeOnEscape(this);
|
||||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||||
setSize(new Dimension(560, 620));
|
setSize(UIScale.scale(new Dimension(560, 620)));
|
||||||
setMinimumSize(new Dimension(480, 520));
|
setMinimumSize(UIScale.scale(new Dimension(480, 520)));
|
||||||
setLocationRelativeTo(owner);
|
setLocationRelativeTo(owner);
|
||||||
|
|
||||||
if (channel != null) load();
|
if (channel != null) load();
|
||||||
@@ -167,9 +168,9 @@ final class ChannelDialog extends JDialog {
|
|||||||
|
|
||||||
private JPanel header() {
|
private JPanel header() {
|
||||||
JPanel panel = new JPanel(new GridBagLayout());
|
JPanel panel = new JPanel(new GridBagLayout());
|
||||||
panel.setBorder(BorderFactory.createEmptyBorder(8, 8, 0, 8));
|
panel.setBorder(new ScaledEmptyBorder(8, 8, 0, 8));
|
||||||
GridBagConstraints c = new GridBagConstraints();
|
GridBagConstraints c = new GridBagConstraints();
|
||||||
c.insets = new Insets(3, 4, 3, 4);
|
c.insets = UIScale.scale(new Insets(3, 4, 3, 4));
|
||||||
c.anchor = GridBagConstraints.WEST;
|
c.anchor = GridBagConstraints.WEST;
|
||||||
c.fill = GridBagConstraints.HORIZONTAL;
|
c.fill = GridBagConstraints.HORIZONTAL;
|
||||||
|
|
||||||
@@ -248,7 +249,7 @@ final class ChannelDialog extends JDialog {
|
|||||||
|
|
||||||
private JPanel iconField() {
|
private JPanel iconField() {
|
||||||
iconButton.setToolTipText("Set channel icon.");
|
iconButton.setToolTipText("Set channel icon.");
|
||||||
iconButton.setPreferredSize(new Dimension(26, 24));
|
iconButton.setPreferredSize(UIScale.scale(new Dimension(26, 24)));
|
||||||
iconButton.setMargin(new Insets(1, 1, 1, 1));
|
iconButton.setMargin(new Insets(1, 1, 1, 1));
|
||||||
iconButton.addActionListener(a -> chooseIcon());
|
iconButton.addActionListener(a -> chooseIcon());
|
||||||
iconButton.addMouseListener(new MouseAdapter() {
|
iconButton.addMouseListener(new MouseAdapter() {
|
||||||
@@ -262,7 +263,7 @@ final class ChannelDialog extends JDialog {
|
|||||||
if (e.isPopupTrigger()) showIconMenu(e);
|
if (e.isPopupTrigger()) showIconMenu(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
JPanel row = new JPanel(new BorderLayout(4, 0));
|
JPanel row = new JPanel(new BorderLayout(UIScale.scale(4), 0));
|
||||||
row.add(new JLabel("Icon:"), BorderLayout.WEST);
|
row.add(new JLabel("Icon:"), BorderLayout.WEST);
|
||||||
row.add(iconButton, BorderLayout.CENTER);
|
row.add(iconButton, BorderLayout.CENTER);
|
||||||
return row;
|
return row;
|
||||||
@@ -273,7 +274,7 @@ final class ChannelDialog extends JDialog {
|
|||||||
ok.addActionListener(a -> save());
|
ok.addActionListener(a -> save());
|
||||||
cancel.addActionListener(a -> dispose());
|
cancel.addActionListener(a -> dispose());
|
||||||
JPanel panel = new JPanel(new BorderLayout());
|
JPanel panel = new JPanel(new BorderLayout());
|
||||||
panel.setBorder(BorderFactory.createEmptyBorder(0, 8, 8, 8));
|
panel.setBorder(new ScaledEmptyBorder(0, 8, 8, 8));
|
||||||
JPanel right = new JPanel();
|
JPanel right = new JPanel();
|
||||||
right.add(ok);
|
right.add(ok);
|
||||||
right.add(cancel);
|
right.add(cancel);
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.ScaledEmptyBorder;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
import javax.swing.BorderFactory;
|
||||||
import javax.swing.Box;
|
import javax.swing.Box;
|
||||||
import javax.swing.BoxLayout;
|
import javax.swing.BoxLayout;
|
||||||
@@ -49,18 +52,18 @@ final class ChannelPermissionsPanel extends JPanel {
|
|||||||
|
|
||||||
ChannelPermissionsPanel() {
|
ChannelPermissionsPanel() {
|
||||||
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
|
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
|
||||||
setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
|
setBorder(new ScaledEmptyBorder(8, 8, 8, 8));
|
||||||
|
|
||||||
JPanel groups = new JPanel();
|
JPanel groups = new JPanel();
|
||||||
groups.setLayout(new BoxLayout(groups, BoxLayout.X_AXIS));
|
groups.setLayout(new BoxLayout(groups, BoxLayout.X_AXIS));
|
||||||
groups.add(group("Regular Needed Powers", REGULAR));
|
groups.add(group("Regular Needed Powers", REGULAR));
|
||||||
groups.add(Box.createHorizontalStrut(8));
|
groups.add(Box.createHorizontalStrut(UIScale.scale(8)));
|
||||||
groups.add(group("File Transfer Needed Powers", FILE_TRANSFER));
|
groups.add(group("File Transfer Needed Powers", FILE_TRANSFER));
|
||||||
groups.setAlignmentX(0f);
|
groups.setAlignmentX(0f);
|
||||||
add(groups);
|
add(groups);
|
||||||
|
|
||||||
status.setAlignmentX(0f);
|
status.setAlignmentX(0f);
|
||||||
status.setBorder(BorderFactory.createEmptyBorder(6, 2, 0, 2));
|
status.setBorder(new ScaledEmptyBorder(6, 2, 0, 2));
|
||||||
add(status);
|
add(status);
|
||||||
add(Box.createVerticalGlue());
|
add(Box.createVerticalGlue());
|
||||||
setEditable(false);
|
setEditable(false);
|
||||||
@@ -119,7 +122,7 @@ final class ChannelPermissionsPanel extends JPanel {
|
|||||||
JPanel panel = new JPanel(new GridBagLayout());
|
JPanel panel = new JPanel(new GridBagLayout());
|
||||||
panel.setBorder(BorderFactory.createTitledBorder(title));
|
panel.setBorder(BorderFactory.createTitledBorder(title));
|
||||||
GridBagConstraints c = new GridBagConstraints();
|
GridBagConstraints c = new GridBagConstraints();
|
||||||
c.insets = new Insets(3, 4, 3, 4);
|
c.insets = UIScale.scale(new Insets(3, 4, 3, 4));
|
||||||
c.anchor = GridBagConstraints.WEST;
|
c.anchor = GridBagConstraints.WEST;
|
||||||
c.fill = GridBagConstraints.HORIZONTAL;
|
c.fill = GridBagConstraints.HORIZONTAL;
|
||||||
int row = 0;
|
int row = 0;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.ScaledEmptyBorder;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.net.ChannelNode;
|
import com.ts3client.net.ChannelNode;
|
||||||
import com.ts3client.net.ChannelSettings;
|
import com.ts3client.net.ChannelSettings;
|
||||||
|
|
||||||
@@ -45,8 +47,8 @@ final class ChannelStandardPanel extends JPanel implements ChannelDialog.Tab {
|
|||||||
* excluding the channel being edited
|
* excluding the channel being edited
|
||||||
*/
|
*/
|
||||||
ChannelStandardPanel(List<ChannelNode> siblings) {
|
ChannelStandardPanel(List<ChannelNode> siblings) {
|
||||||
super(new BorderLayout(8, 8));
|
super(new BorderLayout(UIScale.scale(8), UIScale.scale(8)));
|
||||||
setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
|
setBorder(new ScaledEmptyBorder(8, 8, 8, 8));
|
||||||
|
|
||||||
temporary.setToolTipText("Channel will be deleted when the last user left.");
|
temporary.setToolTipText("Channel will be deleted when the last user left.");
|
||||||
semiPermanent.setToolTipText("Channel exists until server is restarted.");
|
semiPermanent.setToolTipText("Channel exists until server is restarted.");
|
||||||
@@ -67,7 +69,7 @@ final class ChannelStandardPanel extends JPanel implements ChannelDialog.Tab {
|
|||||||
for (ChannelNode sibling : siblings) order.addElement(new SortEntry(sibling.id, sibling.name));
|
for (ChannelNode sibling : siblings) order.addElement(new SortEntry(sibling.id, sibling.name));
|
||||||
sortAfter.setModel(order);
|
sortAfter.setModel(order);
|
||||||
sortAfter.setToolTipText("Channel will be sorted below this channel.");
|
sortAfter.setToolTipText("Channel will be sorted below this channel.");
|
||||||
sortAfter.setPreferredSize(new Dimension(200, sortAfter.getPreferredSize().height));
|
sortAfter.setPreferredSize(new Dimension(UIScale.scale(200), sortAfter.getPreferredSize().height));
|
||||||
Spinners.compact(talkPower);
|
Spinners.compact(talkPower);
|
||||||
|
|
||||||
add(typeBox(), BorderLayout.WEST);
|
add(typeBox(), BorderLayout.WEST);
|
||||||
@@ -139,10 +141,10 @@ final class ChannelStandardPanel extends JPanel implements ChannelDialog.Tab {
|
|||||||
}
|
}
|
||||||
JSeparator line = new JSeparator();
|
JSeparator line = new JSeparator();
|
||||||
line.setAlignmentX(0f);
|
line.setAlignmentX(0f);
|
||||||
line.setMaximumSize(new Dimension(Integer.MAX_VALUE, 8));
|
line.setMaximumSize(new Dimension(Integer.MAX_VALUE, UIScale.scale(8)));
|
||||||
box.add(Box.createVerticalStrut(4));
|
box.add(Box.createVerticalStrut(UIScale.scale(4)));
|
||||||
box.add(line);
|
box.add(line);
|
||||||
box.add(Box.createVerticalStrut(4));
|
box.add(Box.createVerticalStrut(UIScale.scale(4)));
|
||||||
defaultChannel.setAlignmentX(0f);
|
defaultChannel.setAlignmentX(0f);
|
||||||
box.add(defaultChannel);
|
box.add(defaultChannel);
|
||||||
box.add(Box.createVerticalGlue());
|
box.add(Box.createVerticalGlue());
|
||||||
@@ -154,7 +156,7 @@ final class ChannelStandardPanel extends JPanel implements ChannelDialog.Tab {
|
|||||||
sort.setBorder(BorderFactory.createTitledBorder("Sort This Channel After:"));
|
sort.setBorder(BorderFactory.createTitledBorder("Sort This Channel After:"));
|
||||||
sort.add(sortAfter, BorderLayout.CENTER);
|
sort.add(sortAfter, BorderLayout.CENTER);
|
||||||
|
|
||||||
JPanel moderation = new JPanel(new FlowLayout(FlowLayout.LEFT, 6, 2));
|
JPanel moderation = new JPanel(new FlowLayout(FlowLayout.LEFT, UIScale.scale(6), UIScale.scale(2)));
|
||||||
moderation.setBorder(BorderFactory.createTitledBorder("Moderation"));
|
moderation.setBorder(BorderFactory.createTitledBorder("Moderation"));
|
||||||
JLabel label = new JLabel("Needed Talk Power:");
|
JLabel label = new JLabel("Needed Talk Power:");
|
||||||
label.setToolTipText("Talk Power required to speak in this channel.");
|
label.setToolTipText("Talk Power required to speak in this channel.");
|
||||||
@@ -168,7 +170,7 @@ final class ChannelStandardPanel extends JPanel implements ChannelDialog.Tab {
|
|||||||
c.gridx = 0;
|
c.gridx = 0;
|
||||||
c.fill = GridBagConstraints.HORIZONTAL;
|
c.fill = GridBagConstraints.HORIZONTAL;
|
||||||
c.weightx = 1;
|
c.weightx = 1;
|
||||||
c.insets = new Insets(0, 0, 6, 0);
|
c.insets = UIScale.scale(new Insets(0, 0, 6, 0));
|
||||||
column.add(sort, c);
|
column.add(sort, c);
|
||||||
column.add(moderation, c);
|
column.add(moderation, c);
|
||||||
c.weighty = 1;
|
c.weighty = 1;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.ScaledEmptyBorder;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.text.BBCode;
|
import com.ts3client.text.BBCode;
|
||||||
import com.ts3client.text.TsLink;
|
import com.ts3client.text.TsLink;
|
||||||
|
|
||||||
@@ -96,8 +98,8 @@ public final class ChatPanel extends JPanel {
|
|||||||
dragReorder.attach(tabs);
|
dragReorder.attach(tabs);
|
||||||
add(tabs, BorderLayout.CENTER);
|
add(tabs, BorderLayout.CENTER);
|
||||||
|
|
||||||
JPanel bottom = new JPanel(new BorderLayout(4, 0));
|
JPanel bottom = new JPanel(new BorderLayout(UIScale.scale(4), 0));
|
||||||
bottom.setBorder(BorderFactory.createEmptyBorder(4, 0, 0, 0));
|
bottom.setBorder(new ScaledEmptyBorder(4, 0, 0, 0));
|
||||||
bottom.add(input, BorderLayout.CENTER);
|
bottom.add(input, BorderLayout.CENTER);
|
||||||
add(bottom, BorderLayout.SOUTH);
|
add(bottom, BorderLayout.SOUTH);
|
||||||
|
|
||||||
@@ -345,7 +347,8 @@ public final class ChatPanel extends JPanel {
|
|||||||
final int clientId;
|
final int clientId;
|
||||||
private String clientUniqueId;
|
private String clientUniqueId;
|
||||||
private String title;
|
private String title;
|
||||||
final JEditorPane log = HtmlStyles.pane("font-family:sans-serif; font-size:11px; margin:4px 6px;");
|
final JEditorPane log = HtmlStyles.pane("font-family:sans-serif; font-size:11px; margin:"
|
||||||
|
+ HtmlStyles.px(4) + " " + HtmlStyles.px(6) + ";");
|
||||||
final JScrollPane scroll;
|
final JScrollPane scroll;
|
||||||
/** Set for a static-content tab (e.g. a moved-out description); null for a conversation. */
|
/** Set for a static-content tab (e.g. a moved-out description); null for a conversation. */
|
||||||
final String noteKey;
|
final String noteKey;
|
||||||
@@ -408,17 +411,17 @@ public final class ChatPanel extends JPanel {
|
|||||||
Component header(boolean closable) {
|
Component header(boolean closable) {
|
||||||
// Only the height is pinned: a fixed width would freeze the tab at the width of
|
// Only the height is pinned: a fixed width would freeze the tab at the width of
|
||||||
// whatever name it was built with, clipping longer ones (and the bold unread font).
|
// whatever name it was built with, clipping longer ones (and the bold unread font).
|
||||||
JPanel p = new JPanel(new FlowLayout(FlowLayout.LEFT, 2, 0)) {
|
JPanel p = new JPanel(new FlowLayout(FlowLayout.LEFT, UIScale.scale(2), 0)) {
|
||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize() {
|
public Dimension getPreferredSize() {
|
||||||
Dimension d = super.getPreferredSize();
|
Dimension d = super.getPreferredSize();
|
||||||
return new Dimension(d.width, Math.max(d.height, HEADER_HEIGHT));
|
return new Dimension(d.width, Math.max(d.height, UIScale.scale(HEADER_HEIGHT)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
p.setOpaque(false);
|
p.setOpaque(false);
|
||||||
titleLabel = new JLabel(title, icon, JLabel.LEADING);
|
titleLabel = new JLabel(title, icon, JLabel.LEADING);
|
||||||
titleLabel.setFont(Theme.uiFont());
|
titleLabel.setFont(Theme.uiFont());
|
||||||
titleLabel.setIconTextGap(3);
|
titleLabel.setIconTextGap(UIScale.scale(3));
|
||||||
p.add(titleLabel);
|
p.add(titleLabel);
|
||||||
dragReorder.attach(p);
|
dragReorder.attach(p);
|
||||||
dragReorder.attach(titleLabel);
|
dragReorder.attach(titleLabel);
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.ScaledEmptyBorder;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.config.ClientVersion;
|
import com.ts3client.config.ClientVersion;
|
||||||
import com.ts3client.config.Settings;
|
import com.ts3client.config.Settings;
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
|
||||||
import javax.swing.DefaultListCellRenderer;
|
import javax.swing.DefaultListCellRenderer;
|
||||||
import javax.swing.JCheckBox;
|
import javax.swing.JCheckBox;
|
||||||
import javax.swing.JComboBox;
|
import javax.swing.JComboBox;
|
||||||
@@ -45,10 +46,10 @@ final class ClientVersionPanel extends JPanel {
|
|||||||
private boolean filling;
|
private boolean filling;
|
||||||
|
|
||||||
ClientVersionPanel(Settings settings) {
|
ClientVersionPanel(Settings settings) {
|
||||||
super(new BorderLayout(0, 8));
|
super(new BorderLayout(0, UIScale.scale(8)));
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
|
|
||||||
setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));
|
setBorder(new ScaledEmptyBorder(12, 12, 12, 12));
|
||||||
add(buildForm(), BorderLayout.NORTH);
|
add(buildForm(), BorderLayout.NORTH);
|
||||||
|
|
||||||
load();
|
load();
|
||||||
@@ -67,7 +68,7 @@ final class ClientVersionPanel extends JPanel {
|
|||||||
private JComponent buildForm() {
|
private JComponent buildForm() {
|
||||||
JPanel p = new JPanel(new GridBagLayout());
|
JPanel p = new JPanel(new GridBagLayout());
|
||||||
GridBagConstraints c = new GridBagConstraints();
|
GridBagConstraints c = new GridBagConstraints();
|
||||||
c.insets = new Insets(2, 2, 2, 2);
|
c.insets = UIScale.scale(new Insets(2, 2, 2, 2));
|
||||||
c.anchor = GridBagConstraints.WEST;
|
c.anchor = GridBagConstraints.WEST;
|
||||||
c.fill = GridBagConstraints.HORIZONTAL;
|
c.fill = GridBagConstraints.HORIZONTAL;
|
||||||
|
|
||||||
@@ -110,7 +111,7 @@ final class ClientVersionPanel extends JPanel {
|
|||||||
c.gridx = 0;
|
c.gridx = 0;
|
||||||
c.gridy = row;
|
c.gridy = row;
|
||||||
c.gridwidth = 2;
|
c.gridwidth = 2;
|
||||||
p.add(new JLabel("<html><body style='width:320px'>The three fields belong together: the "
|
p.add(new JLabel("<html><body style='width:" + HtmlStyles.px(320) + "'>The three fields belong together: the "
|
||||||
+ "signature is TeamSpeak's, over that version and operating system. A server "
|
+ "signature is TeamSpeak's, over that version and operating system. A server "
|
||||||
+ "answers \"client is modified\" and refuses the connection when it does not "
|
+ "answers \"client is modified\" and refuses the connection when it does not "
|
||||||
+ "accept the version — made up, or older than the server's minimum — so prefer "
|
+ "accept the version — made up, or older than the server's minimum — so prefer "
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.ScaledEmptyBorder;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.net.ClientEntry;
|
import com.ts3client.net.ClientEntry;
|
||||||
import com.ts3client.net.TeamspeakConnection;
|
import com.ts3client.net.TeamspeakConnection;
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JDialog;
|
import javax.swing.JDialog;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
@@ -41,11 +42,11 @@ final class ClientVolumeDialog extends JDialog {
|
|||||||
slider.addChangeListener(e -> onChanged());
|
slider.addChangeListener(e -> onChanged());
|
||||||
FormPanel.limitWidth(value, 60);
|
FormPanel.limitWidth(value, 60);
|
||||||
|
|
||||||
JPanel content = new JPanel(new BorderLayout(8, 8));
|
JPanel content = new JPanel(new BorderLayout(UIScale.scale(8), UIScale.scale(8)));
|
||||||
content.setBorder(BorderFactory.createEmptyBorder(12, 12, 8, 12));
|
content.setBorder(new ScaledEmptyBorder(12, 12, 8, 12));
|
||||||
content.add(new JLabel("Volume Modifier"), BorderLayout.NORTH);
|
content.add(new JLabel("Volume Modifier"), BorderLayout.NORTH);
|
||||||
content.add(FormPanel.sliderWithLabel(slider, value), BorderLayout.CENTER);
|
content.add(FormPanel.sliderWithLabel(slider, value), BorderLayout.CENTER);
|
||||||
JLabel hint = new JLabel("<html><body style='width:300px'>Values greater than +6 dB will cause clipping "
|
JLabel hint = new JLabel("<html><body style='width:" + HtmlStyles.px(300) + "'>Values greater than +6 dB will cause clipping "
|
||||||
+ "and distortion that will negatively affect your audio quality. It is advised to choose "
|
+ "and distortion that will negatively affect your audio quality. It is advised to choose "
|
||||||
+ "lower values.</body></html>");
|
+ "lower values.</body></html>");
|
||||||
hint.setForeground(Theme.chatSystem());
|
hint.setForeground(Theme.chatSystem());
|
||||||
@@ -67,7 +68,7 @@ final class ClientVolumeDialog extends JDialog {
|
|||||||
Dialogs.closeOnEscape(this);
|
Dialogs.closeOnEscape(this);
|
||||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||||
pack();
|
pack();
|
||||||
setMinimumSize(new Dimension(360, getHeight()));
|
setMinimumSize(new Dimension(UIScale.scale(360), getHeight()));
|
||||||
setLocationRelativeTo(owner);
|
setLocationRelativeTo(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.ScaledEmptyBorder;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.config.IdentityStore;
|
import com.ts3client.config.IdentityStore;
|
||||||
import com.ts3client.config.Settings;
|
import com.ts3client.config.Settings;
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JDialog;
|
import javax.swing.JDialog;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
@@ -49,9 +50,9 @@ public final class ConnectDialog extends JDialog {
|
|||||||
identityChooser = new IdentityChooser(identities, false, settings.defaultIdentityId);
|
identityChooser = new IdentityChooser(identities, false, settings.defaultIdentityId);
|
||||||
|
|
||||||
JPanel form = new JPanel(new GridBagLayout());
|
JPanel form = new JPanel(new GridBagLayout());
|
||||||
form.setBorder(BorderFactory.createEmptyBorder(12, 12, 8, 12));
|
form.setBorder(new ScaledEmptyBorder(12, 12, 8, 12));
|
||||||
GridBagConstraints c = new GridBagConstraints();
|
GridBagConstraints c = new GridBagConstraints();
|
||||||
c.insets = new Insets(4, 4, 4, 4);
|
c.insets = UIScale.scale(new Insets(4, 4, 4, 4));
|
||||||
c.anchor = GridBagConstraints.WEST;
|
c.anchor = GridBagConstraints.WEST;
|
||||||
c.fill = GridBagConstraints.HORIZONTAL;
|
c.fill = GridBagConstraints.HORIZONTAL;
|
||||||
|
|
||||||
@@ -83,7 +84,7 @@ public final class ConnectDialog extends JDialog {
|
|||||||
Dialogs.closeOnEscape(this);
|
Dialogs.closeOnEscape(this);
|
||||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||||
pack();
|
pack();
|
||||||
setMinimumSize(new Dimension(340, getHeight()));
|
setMinimumSize(new Dimension(UIScale.scale(340), getHeight()));
|
||||||
setLocationRelativeTo(owner);
|
setLocationRelativeTo(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.ScaledEmptyBorder;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.net.ConnectionStats;
|
import com.ts3client.net.ConnectionStats;
|
||||||
import com.ts3client.net.TeamspeakConnection;
|
import com.ts3client.net.TeamspeakConnection;
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
|
||||||
import javax.swing.Box;
|
import javax.swing.Box;
|
||||||
import javax.swing.BoxLayout;
|
import javax.swing.BoxLayout;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
@@ -62,9 +63,9 @@ public final class ConnectionInfoDialog extends JDialog {
|
|||||||
this.conn = conn;
|
this.conn = conn;
|
||||||
this.clientId = clientId;
|
this.clientId = clientId;
|
||||||
|
|
||||||
JPanel content = new JPanel(new BorderLayout(0, 10));
|
JPanel content = new JPanel(new BorderLayout(0, UIScale.scale(10)));
|
||||||
content.setBackground(Theme.windowBg());
|
content.setBackground(Theme.windowBg());
|
||||||
content.setBorder(BorderFactory.createEmptyBorder(12, 14, 12, 14));
|
content.setBorder(new ScaledEmptyBorder(12, 14, 12, 14));
|
||||||
content.add(buildSummary(), BorderLayout.NORTH);
|
content.add(buildSummary(), BorderLayout.NORTH);
|
||||||
content.add(buildTabs(), BorderLayout.CENTER);
|
content.add(buildTabs(), BorderLayout.CENTER);
|
||||||
content.add(buildButtons(), BorderLayout.SOUTH);
|
content.add(buildButtons(), BorderLayout.SOUTH);
|
||||||
@@ -138,7 +139,7 @@ public final class ConnectionInfoDialog extends JDialog {
|
|||||||
lc.gridx = 0;
|
lc.gridx = 0;
|
||||||
lc.gridy = row;
|
lc.gridy = row;
|
||||||
lc.anchor = GridBagConstraints.WEST;
|
lc.anchor = GridBagConstraints.WEST;
|
||||||
lc.insets = new Insets(2, 0, 2, 16);
|
lc.insets = UIScale.scale(new Insets(2, 0, 2, 16));
|
||||||
JLabel key = new JLabel(label);
|
JLabel key = new JLabel(label);
|
||||||
key.setFont(Theme.uiFont());
|
key.setFont(Theme.uiFont());
|
||||||
key.setForeground(new Color(0x5A6B7B));
|
key.setForeground(new Color(0x5A6B7B));
|
||||||
@@ -150,7 +151,7 @@ public final class ConnectionInfoDialog extends JDialog {
|
|||||||
vc.weightx = 1;
|
vc.weightx = 1;
|
||||||
vc.anchor = GridBagConstraints.WEST;
|
vc.anchor = GridBagConstraints.WEST;
|
||||||
vc.fill = GridBagConstraints.HORIZONTAL;
|
vc.fill = GridBagConstraints.HORIZONTAL;
|
||||||
vc.insets = new Insets(2, 0, 2, 0);
|
vc.insets = UIScale.scale(new Insets(2, 0, 2, 0));
|
||||||
grid.add(valueLabel, vc);
|
grid.add(valueLabel, vc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,7 +257,7 @@ public final class ConnectionInfoDialog extends JDialog {
|
|||||||
KindTab() {
|
KindTab() {
|
||||||
super(new GridBagLayout());
|
super(new GridBagLayout());
|
||||||
setBackground(Theme.windowBg());
|
setBackground(Theme.windowBg());
|
||||||
setBorder(BorderFactory.createEmptyBorder(10, 12, 10, 12));
|
setBorder(new ScaledEmptyBorder(10, 12, 10, 12));
|
||||||
int row = 0;
|
int row = 0;
|
||||||
addRow(this, row++, "Packet loss", packetLoss);
|
addRow(this, row++, "Packet loss", packetLoss);
|
||||||
addRow(this, row++, "Packets (↑/↓)", packets);
|
addRow(this, row++, "Packets (↑/↓)", packets);
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.ScaledEmptyBorder;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.config.Settings;
|
import com.ts3client.config.Settings;
|
||||||
import com.ts3client.contacts.ContactDefaults;
|
import com.ts3client.contacts.ContactDefaults;
|
||||||
import com.ts3client.contacts.ContactType;
|
import com.ts3client.contacts.ContactType;
|
||||||
@@ -50,13 +52,13 @@ final class ContactDefaultsDialog extends JDialog {
|
|||||||
|
|
||||||
JPanel content = new JPanel();
|
JPanel content = new JPanel();
|
||||||
content.setLayout(new javax.swing.BoxLayout(content, javax.swing.BoxLayout.Y_AXIS));
|
content.setLayout(new javax.swing.BoxLayout(content, javax.swing.BoxLayout.Y_AXIS));
|
||||||
content.setBorder(BorderFactory.createEmptyBorder(12, 12, 8, 12));
|
content.setBorder(new ScaledEmptyBorder(12, 12, 8, 12));
|
||||||
JLabel intro = new JLabel("Configure default settings when adding a client to the Contact Manager");
|
JLabel intro = new JLabel("Configure default settings when adding a client to the Contact Manager");
|
||||||
intro.setAlignmentX(LEFT_ALIGNMENT);
|
intro.setAlignmentX(LEFT_ALIGNMENT);
|
||||||
content.add(intro);
|
content.add(intro);
|
||||||
content.add(javax.swing.Box.createVerticalStrut(10));
|
content.add(javax.swing.Box.createVerticalStrut(UIScale.scale(10)));
|
||||||
content.add(buildFlags());
|
content.add(buildFlags());
|
||||||
content.add(javax.swing.Box.createVerticalStrut(10));
|
content.add(javax.swing.Box.createVerticalStrut(UIScale.scale(10)));
|
||||||
content.add(buildDisplays());
|
content.add(buildDisplays());
|
||||||
|
|
||||||
JPanel buttons = new JPanel(new FlowLayout(FlowLayout.RIGHT));
|
JPanel buttons = new JPanel(new FlowLayout(FlowLayout.RIGHT));
|
||||||
@@ -157,7 +159,7 @@ final class ContactDefaultsDialog extends JDialog {
|
|||||||
GridBagConstraints c = new GridBagConstraints();
|
GridBagConstraints c = new GridBagConstraints();
|
||||||
c.gridx = x;
|
c.gridx = x;
|
||||||
c.gridy = y;
|
c.gridy = y;
|
||||||
c.insets = new Insets(1, 6, 1, 6);
|
c.insets = UIScale.scale(new Insets(1, 6, 1, 6));
|
||||||
c.anchor = GridBagConstraints.CENTER;
|
c.anchor = GridBagConstraints.CENTER;
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.ScaledEmptyBorder;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.config.Settings;
|
import com.ts3client.config.Settings;
|
||||||
import com.ts3client.contacts.Contact;
|
import com.ts3client.contacts.Contact;
|
||||||
import com.ts3client.contacts.ContactStore;
|
import com.ts3client.contacts.ContactStore;
|
||||||
@@ -8,7 +10,6 @@ import com.ts3client.contacts.NicknameDisplay;
|
|||||||
import com.ts3client.teamspeak.TeamSpeakSettingsDb;
|
import com.ts3client.teamspeak.TeamSpeakSettingsDb;
|
||||||
import com.ts3client.text.TsLink;
|
import com.ts3client.text.TsLink;
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
|
||||||
import javax.swing.Box;
|
import javax.swing.Box;
|
||||||
import javax.swing.BoxLayout;
|
import javax.swing.BoxLayout;
|
||||||
import javax.swing.ButtonGroup;
|
import javax.swing.ButtonGroup;
|
||||||
@@ -99,7 +100,7 @@ final class ContactsDialog extends JDialog {
|
|||||||
private final JCheckBox ignoreAway = new JCheckBox("Hide away message");
|
private final JCheckBox ignoreAway = new JCheckBox("Hide away message");
|
||||||
private final JRadioButton allowWhisper = new JRadioButton("Allow whisper");
|
private final JRadioButton allowWhisper = new JRadioButton("Allow whisper");
|
||||||
private final JRadioButton denyWhisper = new JRadioButton("Deny whisper");
|
private final JRadioButton denyWhisper = new JRadioButton("Deny whisper");
|
||||||
private final JPanel details = new JPanel(new BorderLayout(0, 8));
|
private final JPanel details = new JPanel(new BorderLayout(0, UIScale.scale(8)));
|
||||||
|
|
||||||
/** Set while the fields are being filled from a contact, so that does not count as an edit. */
|
/** Set while the fields are being filled from a contact, so that does not count as an edit. */
|
||||||
private boolean filling;
|
private boolean filling;
|
||||||
@@ -110,22 +111,22 @@ final class ContactsDialog extends JDialog {
|
|||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
|
|
||||||
buildTree();
|
buildTree();
|
||||||
JPanel left = new JPanel(new BorderLayout(0, 4));
|
JPanel left = new JPanel(new BorderLayout(0, UIScale.scale(4)));
|
||||||
left.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 4));
|
left.setBorder(new ScaledEmptyBorder(8, 8, 8, 4));
|
||||||
JLabel hint = new JLabel("Drag & Drop users here");
|
JLabel hint = new JLabel("Drag & Drop users here");
|
||||||
hint.setForeground(Theme.chatSystem());
|
hint.setForeground(Theme.chatSystem());
|
||||||
left.add(hint, BorderLayout.NORTH);
|
left.add(hint, BorderLayout.NORTH);
|
||||||
JScrollPane scroll = new JScrollPane(tree);
|
JScrollPane scroll = new JScrollPane(tree);
|
||||||
scroll.setPreferredSize(new Dimension(300, 340));
|
scroll.setPreferredSize(UIScale.scale(new Dimension(300, 340)));
|
||||||
left.add(scroll, BorderLayout.CENTER);
|
left.add(scroll, BorderLayout.CENTER);
|
||||||
|
|
||||||
details.setBorder(BorderFactory.createEmptyBorder(8, 4, 8, 8));
|
details.setBorder(new ScaledEmptyBorder(8, 4, 8, 8));
|
||||||
details.add(buildNicknameRow(), BorderLayout.NORTH);
|
details.add(buildNicknameRow(), BorderLayout.NORTH);
|
||||||
details.add(buildPages(), BorderLayout.CENTER);
|
details.add(buildPages(), BorderLayout.CENTER);
|
||||||
|
|
||||||
JPanel buttons = new JPanel(new BorderLayout());
|
JPanel buttons = new JPanel(new BorderLayout());
|
||||||
buttons.setBorder(BorderFactory.createEmptyBorder(0, 8, 8, 8));
|
buttons.setBorder(new ScaledEmptyBorder(0, 8, 8, 8));
|
||||||
JPanel leftButtons = new JPanel(new FlowLayout(FlowLayout.LEFT, 6, 0));
|
JPanel leftButtons = new JPanel(new FlowLayout(FlowLayout.LEFT, UIScale.scale(6), 0));
|
||||||
JButton defaults = new JButton("Set Defaults");
|
JButton defaults = new JButton("Set Defaults");
|
||||||
defaults.setToolTipText("Set default display, ignore and hide options");
|
defaults.setToolTipText("Set default display, ignore and hide options");
|
||||||
defaults.addActionListener(e -> new ContactDefaultsDialog(this, settings).setVisible(true));
|
defaults.addActionListener(e -> new ContactDefaultsDialog(this, settings).setVisible(true));
|
||||||
@@ -160,7 +161,7 @@ final class ContactsDialog extends JDialog {
|
|||||||
Dialogs.closeOnEscape(this);
|
Dialogs.closeOnEscape(this);
|
||||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||||
pack();
|
pack();
|
||||||
setMinimumSize(new Dimension(620, 400));
|
setMinimumSize(UIScale.scale(new Dimension(620, 400)));
|
||||||
setLocationRelativeTo(owner);
|
setLocationRelativeTo(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,7 +173,7 @@ final class ContactsDialog extends JDialog {
|
|||||||
root.add(neutral);
|
root.add(neutral);
|
||||||
tree.setRootVisible(false);
|
tree.setRootVisible(false);
|
||||||
tree.setShowsRootHandles(true);
|
tree.setShowsRootHandles(true);
|
||||||
tree.setRowHeight(20);
|
tree.setRowHeight(UIScale.scale(20));
|
||||||
tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
|
tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
|
||||||
tree.setCellRenderer(new ContactRenderer());
|
tree.setCellRenderer(new ContactRenderer());
|
||||||
tree.addTreeSelectionListener(e -> showSelected());
|
tree.addTreeSelectionListener(e -> showSelected());
|
||||||
@@ -301,7 +302,7 @@ final class ContactsDialog extends JDialog {
|
|||||||
private JComponent buildNicknameRow() {
|
private JComponent buildNicknameRow() {
|
||||||
JPanel row = new JPanel(new GridBagLayout());
|
JPanel row = new JPanel(new GridBagLayout());
|
||||||
GridBagConstraints c = new GridBagConstraints();
|
GridBagConstraints c = new GridBagConstraints();
|
||||||
c.insets = new Insets(2, 2, 2, 2);
|
c.insets = UIScale.scale(new Insets(2, 2, 2, 2));
|
||||||
c.anchor = GridBagConstraints.WEST;
|
c.anchor = GridBagConstraints.WEST;
|
||||||
c.gridy = 0;
|
c.gridy = 0;
|
||||||
c.gridx = 0;
|
c.gridx = 0;
|
||||||
@@ -325,7 +326,7 @@ final class ContactsDialog extends JDialog {
|
|||||||
JTabbedPane pages = new JTabbedPane();
|
JTabbedPane pages = new JTabbedPane();
|
||||||
pages.addTab("General", page(
|
pages.addTab("General", page(
|
||||||
radioGroup(neutralRadio, blockedRadio, friendRadio),
|
radioGroup(neutralRadio, blockedRadio, friendRadio),
|
||||||
Box.createVerticalStrut(8),
|
Box.createVerticalStrut(UIScale.scale(8)),
|
||||||
labelled("Phonetic Nickname:", phoneticField)));
|
labelled("Phonetic Nickname:", phoneticField)));
|
||||||
neutralRadio.setToolTipText("Mark as neutral");
|
neutralRadio.setToolTipText("Mark as neutral");
|
||||||
blockedRadio.setToolTipText("Mark as blocked");
|
blockedRadio.setToolTipText("Mark as blocked");
|
||||||
@@ -375,7 +376,7 @@ final class ContactsDialog extends JDialog {
|
|||||||
private static JPanel page(Component... rows) {
|
private static JPanel page(Component... rows) {
|
||||||
JPanel p = new JPanel();
|
JPanel p = new JPanel();
|
||||||
p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
|
p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
|
||||||
p.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
p.setBorder(new ScaledEmptyBorder(10, 10, 10, 10));
|
||||||
for (Component row : rows) {
|
for (Component row : rows) {
|
||||||
if (row instanceof JComponent) ((JComponent) row).setAlignmentX(Component.LEFT_ALIGNMENT);
|
if (row instanceof JComponent) ((JComponent) row).setAlignmentX(Component.LEFT_ALIGNMENT);
|
||||||
p.add(row);
|
p.add(row);
|
||||||
@@ -397,7 +398,7 @@ final class ContactsDialog extends JDialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static JPanel labelled(String label, JComponent field) {
|
private static JPanel labelled(String label, JComponent field) {
|
||||||
JPanel p = new JPanel(new BorderLayout(6, 0));
|
JPanel p = new JPanel(new BorderLayout(UIScale.scale(6), 0));
|
||||||
p.add(new JLabel(label), BorderLayout.WEST);
|
p.add(new JLabel(label), BorderLayout.WEST);
|
||||||
p.add(field, BorderLayout.CENTER);
|
p.add(field, BorderLayout.CENTER);
|
||||||
p.setMaximumSize(new Dimension(Integer.MAX_VALUE, field.getPreferredSize().height));
|
p.setMaximumSize(new Dimension(Integer.MAX_VALUE, field.getPreferredSize().height));
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.ScaledEmptyBorder;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.config.Settings;
|
import com.ts3client.config.Settings;
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
|
||||||
import javax.swing.DefaultListCellRenderer;
|
import javax.swing.DefaultListCellRenderer;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JCheckBox;
|
import javax.swing.JCheckBox;
|
||||||
@@ -32,11 +33,11 @@ final class ContactsPanel extends JPanel {
|
|||||||
ContactsPanel(Settings settings) {
|
ContactsPanel(Settings settings) {
|
||||||
super(new BorderLayout());
|
super(new BorderLayout());
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));
|
setBorder(new ScaledEmptyBorder(12, 12, 12, 12));
|
||||||
|
|
||||||
JPanel p = new JPanel(new GridBagLayout());
|
JPanel p = new JPanel(new GridBagLayout());
|
||||||
GridBagConstraints c = new GridBagConstraints();
|
GridBagConstraints c = new GridBagConstraints();
|
||||||
c.insets = new Insets(2, 2, 2, 2);
|
c.insets = UIScale.scale(new Insets(2, 2, 2, 2));
|
||||||
c.anchor = GridBagConstraints.WEST;
|
c.anchor = GridBagConstraints.WEST;
|
||||||
c.fill = GridBagConstraints.HORIZONTAL;
|
c.fill = GridBagConstraints.HORIZONTAL;
|
||||||
c.gridx = 0;
|
c.gridx = 0;
|
||||||
@@ -48,10 +49,10 @@ final class ContactsPanel extends JPanel {
|
|||||||
p.add(colorContacts, c);
|
p.add(colorContacts, c);
|
||||||
|
|
||||||
c.gridy = 1;
|
c.gridy = 1;
|
||||||
c.insets = new Insets(12, 2, 2, 2);
|
c.insets = UIScale.scale(new Insets(12, 2, 2, 2));
|
||||||
p.add(new JLabel("Whispers: who is allowed to whisper to you"), c);
|
p.add(new JLabel("Whispers: who is allowed to whisper to you"), c);
|
||||||
c.gridy = 2;
|
c.gridy = 2;
|
||||||
c.insets = new Insets(2, 2, 2, 2);
|
c.insets = UIScale.scale(new Insets(2, 2, 2, 2));
|
||||||
whisperPolicy.setSelectedItem(settings.whisperPolicy);
|
whisperPolicy.setSelectedItem(settings.whisperPolicy);
|
||||||
whisperPolicy.setRenderer(new DefaultListCellRenderer() {
|
whisperPolicy.setRenderer(new DefaultListCellRenderer() {
|
||||||
@Override
|
@Override
|
||||||
@@ -68,7 +69,7 @@ final class ContactsPanel extends JPanel {
|
|||||||
p.add(whisperPolicy, c);
|
p.add(whisperPolicy, c);
|
||||||
|
|
||||||
c.gridy = 3;
|
c.gridy = 3;
|
||||||
c.insets = new Insets(12, 2, 2, 2);
|
c.insets = UIScale.scale(new Insets(12, 2, 2, 2));
|
||||||
c.fill = GridBagConstraints.NONE;
|
c.fill = GridBagConstraints.NONE;
|
||||||
JButton defaults = new JButton("Contact defaults…");
|
JButton defaults = new JButton("Contact defaults…");
|
||||||
defaults.setToolTipText("Configure default settings when adding a client to the Contact Manager");
|
defaults.setToolTipText("Configure default settings when adding a client to the Contact Manager");
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.ScaledEmptyBorder;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.text.BBCode;
|
import com.ts3client.text.BBCode;
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
|
||||||
import javax.swing.Box;
|
import javax.swing.Box;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JColorChooser;
|
import javax.swing.JColorChooser;
|
||||||
@@ -47,19 +48,19 @@ final class DescriptionEditorDialog extends JDialog {
|
|||||||
area.setText(description);
|
area.setText(description);
|
||||||
area.setLineWrap(true);
|
area.setLineWrap(true);
|
||||||
area.setWrapStyleWord(true);
|
area.setWrapStyleWord(true);
|
||||||
area.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
|
area.setFont(new Font(Font.MONOSPACED, Font.PLAIN, UIScale.scale(12)));
|
||||||
area.setCaretPosition(0);
|
area.setCaretPosition(0);
|
||||||
|
|
||||||
preview.setEditable(false);
|
preview.setEditable(false);
|
||||||
preview.setBackground(Theme.chatBg());
|
preview.setBackground(Theme.chatBg());
|
||||||
preview.setBorder(BorderFactory.createEmptyBorder(6, 8, 6, 8));
|
preview.setBorder(new ScaledEmptyBorder(6, 8, 6, 8));
|
||||||
|
|
||||||
body.add(new JScrollPane(area), EDIT_CARD);
|
body.add(new JScrollPane(area), EDIT_CARD);
|
||||||
body.add(new JScrollPane(preview), PREVIEW_CARD);
|
body.add(new JScrollPane(preview), PREVIEW_CARD);
|
||||||
|
|
||||||
JLabel hint = new JLabel("Press Button to \"Preview\" the changes in channel info.");
|
JLabel hint = new JLabel("Press Button to \"Preview\" the changes in channel info.");
|
||||||
hint.setForeground(Theme.chatSystem());
|
hint.setForeground(Theme.chatSystem());
|
||||||
hint.setBorder(BorderFactory.createEmptyBorder(4, 6, 0, 6));
|
hint.setBorder(new ScaledEmptyBorder(4, 6, 0, 6));
|
||||||
|
|
||||||
getContentPane().setLayout(new BorderLayout());
|
getContentPane().setLayout(new BorderLayout());
|
||||||
getContentPane().add(buildToolbar(), BorderLayout.NORTH);
|
getContentPane().add(buildToolbar(), BorderLayout.NORTH);
|
||||||
@@ -71,8 +72,8 @@ final class DescriptionEditorDialog extends JDialog {
|
|||||||
|
|
||||||
Dialogs.closeOnEscape(this);
|
Dialogs.closeOnEscape(this);
|
||||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||||
setSize(new Dimension(520, 380));
|
setSize(UIScale.scale(new Dimension(520, 380)));
|
||||||
setMinimumSize(new Dimension(360, 260));
|
setMinimumSize(UIScale.scale(new Dimension(360, 260)));
|
||||||
setLocationRelativeTo(owner);
|
setLocationRelativeTo(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,7 +135,7 @@ final class DescriptionEditorDialog extends JDialog {
|
|||||||
* box, which would otherwise let one of them soak up all the free width.
|
* box, which would otherwise let one of them soak up all the free width.
|
||||||
*/
|
*/
|
||||||
private static JButton square(JButton button) {
|
private static JButton square(JButton button) {
|
||||||
Dimension size = new Dimension(Math.max(28, button.getPreferredSize().width),
|
Dimension size = new Dimension(Math.max(UIScale.scale(28), button.getPreferredSize().width),
|
||||||
button.getPreferredSize().height);
|
button.getPreferredSize().height);
|
||||||
button.setPreferredSize(size);
|
button.setPreferredSize(size);
|
||||||
button.setMaximumSize(size);
|
button.setMaximumSize(size);
|
||||||
@@ -162,7 +163,7 @@ final class DescriptionEditorDialog extends JDialog {
|
|||||||
private void showPreview(boolean on) {
|
private void showPreview(boolean on) {
|
||||||
previewButton.setSelected(on);
|
previewButton.setSelected(on);
|
||||||
if (on) {
|
if (on) {
|
||||||
preview.setText("<html><body style=\"font-family:sans-serif;font-size:9pt\">"
|
preview.setText("<html><body style=\"font-family:sans-serif;font-size:" + UIScale.scale(9) + "pt\">"
|
||||||
+ BBCode.toHtml(area.getText()) + "</body></html>");
|
+ BBCode.toHtml(area.getText()) + "</body></html>");
|
||||||
preview.setCaretPosition(0);
|
preview.setCaretPosition(0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.ScaledEmptyBorder;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.config.Settings;
|
import com.ts3client.config.Settings;
|
||||||
import com.ts3client.gfx.IconPack;
|
import com.ts3client.gfx.IconPack;
|
||||||
|
|
||||||
@@ -24,14 +26,15 @@ import java.awt.GridBagConstraints;
|
|||||||
import java.awt.GridBagLayout;
|
import java.awt.GridBagLayout;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Options page for how the client looks: the light or dark look-and-feel, and the icon
|
* Options page for how the client looks: the light or dark look-and-feel, the interface
|
||||||
* pack the user interface is drawn with — where to look for more of them, and a viewer
|
* scale, and the icon pack the user interface is drawn with — where to look for more of
|
||||||
* showing everything the active pack contains.
|
* them, and a viewer showing everything the active pack contains.
|
||||||
*
|
*
|
||||||
* <p>Both apply right away so the change can be seen in the window behind the dialog;
|
* <p>Theme and pack apply right away so the change can be seen in the window behind the
|
||||||
* cancelling puts the previous ones back.
|
* dialog; cancelling puts the previous ones back. The scale is only read at startup.
|
||||||
*/
|
*/
|
||||||
final class DesignPanel extends JPanel {
|
final class DesignPanel extends JPanel {
|
||||||
|
|
||||||
@@ -42,6 +45,9 @@ final class DesignPanel extends JPanel {
|
|||||||
/** Stands for "no pack": the icons the client draws itself. */
|
/** Stands for "no pack": the icons the client draws itself. */
|
||||||
private static final String BUILT_IN = "Built-in icons";
|
private static final String BUILT_IN = "Built-in icons";
|
||||||
|
|
||||||
|
/** The scale factors offered besides following the desktop (0). */
|
||||||
|
private static final double[] SCALES = {0, 1, 1.25, 1.5, 1.75, 2, 2.5, 3};
|
||||||
|
|
||||||
private final Settings settings;
|
private final Settings settings;
|
||||||
private final String originalPackId;
|
private final String originalPackId;
|
||||||
private final String originalPackDir;
|
private final String originalPackDir;
|
||||||
@@ -49,6 +55,7 @@ final class DesignPanel extends JPanel {
|
|||||||
|
|
||||||
private final JComboBox<Settings.Appearance> appearanceCombo =
|
private final JComboBox<Settings.Appearance> appearanceCombo =
|
||||||
new JComboBox<>(Settings.Appearance.values());
|
new JComboBox<>(Settings.Appearance.values());
|
||||||
|
private final JComboBox<Double> scaleCombo = new JComboBox<>();
|
||||||
private final JComboBox<Object> packCombo = new JComboBox<>();
|
private final JComboBox<Object> packCombo = new JComboBox<>();
|
||||||
private final JLabel packInfo = new JLabel();
|
private final JLabel packInfo = new JLabel();
|
||||||
private final JTextField packDirField;
|
private final JTextField packDirField;
|
||||||
@@ -56,14 +63,14 @@ final class DesignPanel extends JPanel {
|
|||||||
private final JList<String> preview = new JList<>(previewModel);
|
private final JList<String> preview = new JList<>(previewModel);
|
||||||
|
|
||||||
DesignPanel(Settings settings) {
|
DesignPanel(Settings settings) {
|
||||||
super(new BorderLayout(0, 8));
|
super(new BorderLayout(0, UIScale.scale(8)));
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
this.originalPackId = settings.iconPack;
|
this.originalPackId = settings.iconPack;
|
||||||
this.originalPackDir = settings.iconPackDir;
|
this.originalPackDir = settings.iconPackDir;
|
||||||
this.originalAppearance = settings.appearance;
|
this.originalAppearance = settings.appearance;
|
||||||
this.packDirField = new JTextField(settings.iconPackDir, 18);
|
this.packDirField = new JTextField(settings.iconPackDir, 18);
|
||||||
|
|
||||||
setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));
|
setBorder(new ScaledEmptyBorder(12, 12, 12, 12));
|
||||||
add(buildHeader(), BorderLayout.NORTH);
|
add(buildHeader(), BorderLayout.NORTH);
|
||||||
add(buildPreview(), BorderLayout.CENTER);
|
add(buildPreview(), BorderLayout.CENTER);
|
||||||
|
|
||||||
@@ -73,6 +80,7 @@ final class DesignPanel extends JPanel {
|
|||||||
/** Copies the chosen appearance and pack into the settings; the caller saves them. */
|
/** Copies the chosen appearance and pack into the settings; the caller saves them. */
|
||||||
void apply() {
|
void apply() {
|
||||||
settings.appearance = selectedAppearance();
|
settings.appearance = selectedAppearance();
|
||||||
|
if (scaleCombo.getSelectedItem() instanceof Double scale) settings.uiScale = scale;
|
||||||
settings.iconPackDir = packDirField.getText().trim();
|
settings.iconPackDir = packDirField.getText().trim();
|
||||||
Object selected = packCombo.getSelectedItem();
|
Object selected = packCombo.getSelectedItem();
|
||||||
settings.iconPack = selected instanceof IconPack pack ? pack.id() : "";
|
settings.iconPack = selected instanceof IconPack pack ? pack.id() : "";
|
||||||
@@ -94,7 +102,7 @@ final class DesignPanel extends JPanel {
|
|||||||
private JComponent buildHeader() {
|
private JComponent buildHeader() {
|
||||||
JPanel p = new JPanel(new GridBagLayout());
|
JPanel p = new JPanel(new GridBagLayout());
|
||||||
GridBagConstraints c = new GridBagConstraints();
|
GridBagConstraints c = new GridBagConstraints();
|
||||||
c.insets = new Insets(2, 2, 2, 2);
|
c.insets = UIScale.scale(new Insets(2, 2, 2, 2));
|
||||||
c.anchor = GridBagConstraints.WEST;
|
c.anchor = GridBagConstraints.WEST;
|
||||||
c.fill = GridBagConstraints.HORIZONTAL;
|
c.fill = GridBagConstraints.HORIZONTAL;
|
||||||
|
|
||||||
@@ -125,13 +133,17 @@ final class DesignPanel extends JPanel {
|
|||||||
|
|
||||||
int row = 0;
|
int row = 0;
|
||||||
addRow(p, c, row++, new JLabel("Theme:"), appearanceCombo);
|
addRow(p, c, row++, new JLabel("Theme:"), appearanceCombo);
|
||||||
|
if (DisplayScale.isManaged()) {
|
||||||
|
buildScaleCombo();
|
||||||
|
addRow(p, c, row++, new JLabel("Interface scale:"), scaleCombo);
|
||||||
|
}
|
||||||
addRow(p, c, row++, new JLabel("Icon pack:"), packCombo);
|
addRow(p, c, row++, new JLabel("Icon pack:"), packCombo);
|
||||||
c.gridx = 1;
|
c.gridx = 1;
|
||||||
c.gridy = row++;
|
c.gridy = row++;
|
||||||
packInfo.setEnabled(false);
|
packInfo.setEnabled(false);
|
||||||
p.add(packInfo, c);
|
p.add(packInfo, c);
|
||||||
|
|
||||||
JPanel dirRow = new JPanel(new BorderLayout(6, 0));
|
JPanel dirRow = new JPanel(new BorderLayout(UIScale.scale(6), 0));
|
||||||
JButton browse = new JButton("Browse…");
|
JButton browse = new JButton("Browse…");
|
||||||
browse.addActionListener(e -> browseForPackFolder());
|
browse.addActionListener(e -> browseForPackFolder());
|
||||||
dirRow.add(packDirField, BorderLayout.CENTER);
|
dirRow.add(packDirField, BorderLayout.CENTER);
|
||||||
@@ -141,18 +153,42 @@ final class DesignPanel extends JPanel {
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void buildScaleCombo() {
|
||||||
|
for (double scale : SCALES) scaleCombo.addItem(scale);
|
||||||
|
if (settings.uiScale > 0 && Arrays.stream(SCALES).noneMatch(s -> s == settings.uiScale)) {
|
||||||
|
scaleCombo.addItem(settings.uiScale);
|
||||||
|
}
|
||||||
|
scaleCombo.setSelectedItem(Math.max(0, settings.uiScale));
|
||||||
|
scaleCombo.setToolTipText("Size of text, icons and controls; takes effect after a restart");
|
||||||
|
scaleCombo.setRenderer(new DefaultListCellRenderer() {
|
||||||
|
@Override
|
||||||
|
public Component getListCellRendererComponent(JList<?> list, Object value, int index,
|
||||||
|
boolean selected, boolean focus) {
|
||||||
|
super.getListCellRendererComponent(list, value, index, selected, focus);
|
||||||
|
double scale = value instanceof Double d ? d : 0;
|
||||||
|
setText(scale > 0 ? percent(scale)
|
||||||
|
: "Automatic (" + percent(DisplayScale.detected()) + ")");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String percent(double scale) {
|
||||||
|
return Math.round(scale * 100) + "%";
|
||||||
|
}
|
||||||
|
|
||||||
private JComponent buildPreview() {
|
private JComponent buildPreview() {
|
||||||
preview.setLayoutOrientation(JList.HORIZONTAL_WRAP);
|
preview.setLayoutOrientation(JList.HORIZONTAL_WRAP);
|
||||||
preview.setVisibleRowCount(-1);
|
preview.setVisibleRowCount(-1);
|
||||||
preview.setFixedCellWidth(TILE);
|
preview.setFixedCellWidth(UIScale.scale(TILE));
|
||||||
preview.setFixedCellHeight(TILE);
|
preview.setFixedCellHeight(UIScale.scale(TILE));
|
||||||
preview.setCellRenderer(new DefaultListCellRenderer() {
|
preview.setCellRenderer(new DefaultListCellRenderer() {
|
||||||
@Override
|
@Override
|
||||||
public Component getListCellRendererComponent(JList<?> list, Object value, int index,
|
public Component getListCellRendererComponent(JList<?> list, Object value, int index,
|
||||||
boolean selected, boolean focus) {
|
boolean selected, boolean focus) {
|
||||||
super.getListCellRendererComponent(list, value, index, selected, focus);
|
super.getListCellRendererComponent(list, value, index, selected, focus);
|
||||||
String key = String.valueOf(value);
|
String key = String.valueOf(value);
|
||||||
ImageIcon icon = Icons.of(key, PREVIEW_SIZE);
|
ImageIcon icon = Icons.of(key, UIScale.scale(PREVIEW_SIZE));
|
||||||
setText("");
|
setText("");
|
||||||
setIcon(icon);
|
setIcon(icon);
|
||||||
setToolTipText(key);
|
setToolTipText(key);
|
||||||
@@ -162,7 +198,7 @@ final class DesignPanel extends JPanel {
|
|||||||
});
|
});
|
||||||
|
|
||||||
JScrollPane scroll = new JScrollPane(preview);
|
JScrollPane scroll = new JScrollPane(preview);
|
||||||
scroll.setPreferredSize(new Dimension(360, 200));
|
scroll.setPreferredSize(UIScale.scale(new Dimension(360, 200)));
|
||||||
scroll.setBorder(BorderFactory.createTitledBorder("Icons in this pack"));
|
scroll.setBorder(BorderFactory.createTitledBorder("Icons in this pack"));
|
||||||
return scroll;
|
return scroll;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.audio.VoiceInput;
|
import com.ts3client.audio.VoiceInput;
|
||||||
import com.ts3client.audio.VoiceOutput;
|
import com.ts3client.audio.VoiceOutput;
|
||||||
import com.ts3client.audio.desktop.AudioDevices;
|
import com.ts3client.audio.desktop.AudioDevices;
|
||||||
@@ -75,9 +76,9 @@ final class DevicesPanel extends FormPanel {
|
|||||||
c.gridx = 0;
|
c.gridx = 0;
|
||||||
c.gridy = row++;
|
c.gridy = row++;
|
||||||
c.gridwidth = 2;
|
c.gridwidth = 2;
|
||||||
c.insets = new Insets(14, 4, 2, 4);
|
c.insets = UIScale.scale(new Insets(14, 4, 2, 4));
|
||||||
add(new JLabel("Noise reduction"), c);
|
add(new JLabel("Noise reduction"), c);
|
||||||
c.insets = new Insets(4, 4, 4, 4);
|
c.insets = UIScale.scale(new Insets(4, 4, 4, 4));
|
||||||
c.gridwidth = 1;
|
c.gridwidth = 1;
|
||||||
|
|
||||||
denoiseCheck = new JCheckBox("Remove background noise", settings.denoise);
|
denoiseCheck = new JCheckBox("Remove background noise", settings.denoise);
|
||||||
|
|||||||
@@ -0,0 +1,134 @@
|
|||||||
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import java.awt.Toolkit;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decides how large the user interface is drawn on Linux, where Java2D only knows
|
||||||
|
* whole-number scale factors: asked for 150 %, it draws at 100 % — or, told
|
||||||
|
* 2 by the desktop, at 200 %, while FlatLaf's own scaling goes on top of that.
|
||||||
|
*
|
||||||
|
* <p>So on Linux Java2D is pinned to 1 and the whole factor, fractional or not, is
|
||||||
|
* handed to FlatLaf, which scales fonts and its own metrics; the client scales its own
|
||||||
|
* sizes through FlatLaf's {@link com.formdev.flatlaf.util.UIScale} to match.
|
||||||
|
*
|
||||||
|
* <p>The desktop's factor is read the way an X11 toolkit would learn it:
|
||||||
|
* <ol>
|
||||||
|
* <li>{@code Xft/DPI} from XSETTINGS — GNOME and Plasma (through xsettingsd) publish
|
||||||
|
* the device-pixel DPI there, fractional scale and text scaling included;</li>
|
||||||
|
* <li>KWin's {@code [Xwayland] Scale}, which Plasma sets when X11 apps are left to
|
||||||
|
* scale themselves — for sessions with no XSETTINGS daemon running;</li>
|
||||||
|
* <li>{@code GDK_SCALE}, which Java2D would otherwise have honoured itself.</li>
|
||||||
|
* </ol>
|
||||||
|
* With the compositor scaling X11 apps (GNOME's default, and Plasma's "Scaled by the
|
||||||
|
* system") the client sees 96 DPI and is drawn at 100 %, which is what those
|
||||||
|
* modes expect; they then upscale the window, blurry but the right size.
|
||||||
|
*
|
||||||
|
* <p>A {@code sun.java2d.uiScale} or {@code flatlaf.uiScale} given on the command line
|
||||||
|
* turns all of this off.
|
||||||
|
*/
|
||||||
|
public final class DisplayScale {
|
||||||
|
|
||||||
|
private static final String JAVA2D_SCALE = "sun.java2d.uiScale";
|
||||||
|
private static final String FLATLAF_SCALE = "flatlaf.uiScale";
|
||||||
|
private static final double BASE_DPI = 96;
|
||||||
|
private static final double MAX_SCALE = 4;
|
||||||
|
|
||||||
|
private static boolean managed;
|
||||||
|
private static double detected = 1;
|
||||||
|
|
||||||
|
private DisplayScale() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the scale up; call before anything touches AWT.
|
||||||
|
*
|
||||||
|
* @param override the user's chosen factor, or 0 to follow the desktop
|
||||||
|
*/
|
||||||
|
public static void configure(double override) {
|
||||||
|
if (!isLinux() || System.getProperty(JAVA2D_SCALE) != null
|
||||||
|
|| System.getProperty(FLATLAF_SCALE) != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
System.setProperty(JAVA2D_SCALE, "1");
|
||||||
|
managed = true;
|
||||||
|
detected = detect();
|
||||||
|
double factor = override > 0 ? override : detected;
|
||||||
|
System.setProperty(FLATLAF_SCALE, Double.toString(factor));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Whether the client sets the scale itself, so that the user may choose one. */
|
||||||
|
public static boolean isManaged() {
|
||||||
|
return managed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The desktop's factor, as found at startup. */
|
||||||
|
public static double detected() {
|
||||||
|
return detected;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double detect() {
|
||||||
|
double scale = xftScale();
|
||||||
|
if (scale <= 0) scale = kwinXwaylandScale();
|
||||||
|
scale = Math.max(scale, gdkScale());
|
||||||
|
return Math.max(1, Math.min(MAX_SCALE, scale));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double xftScale() {
|
||||||
|
// The XSETTINGS value is in 1024ths of a DPI; -1 means "unset".
|
||||||
|
Object dpi = Toolkit.getDefaultToolkit().getDesktopProperty("gnome.Xft/DPI");
|
||||||
|
return dpi instanceof Integer d && d > 0 ? d / 1024.0 / BASE_DPI : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double kwinXwaylandScale() {
|
||||||
|
// Left behind by a Wayland session, the key would be stale in an X11 one.
|
||||||
|
if (System.getenv("WAYLAND_DISPLAY") == null) return 0;
|
||||||
|
String configHome = System.getenv("XDG_CONFIG_HOME");
|
||||||
|
Path kwinrc = (configHome == null || configHome.isEmpty()
|
||||||
|
? Path.of(System.getProperty("user.home"), ".config")
|
||||||
|
: Path.of(configHome)).resolve("kwinrc");
|
||||||
|
try {
|
||||||
|
return parseDouble(iniValue(Files.readAllLines(kwinrc), "Xwayland", "Scale"));
|
||||||
|
} catch (IOException e) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double gdkScale() {
|
||||||
|
return parseDouble(System.getenv("GDK_SCALE"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The value of {@code key} in {@code [group]} of a KConfig-style file, or {@code null}. */
|
||||||
|
static String iniValue(List<String> lines, String group, String key) {
|
||||||
|
boolean inGroup = false;
|
||||||
|
for (String line : lines) {
|
||||||
|
String t = line.trim();
|
||||||
|
if (t.startsWith("[")) {
|
||||||
|
inGroup = t.equals("[" + group + "]");
|
||||||
|
} else if (inGroup) {
|
||||||
|
int eq = t.indexOf('=');
|
||||||
|
if (eq > 0 && t.substring(0, eq).trim().equals(key)) {
|
||||||
|
return t.substring(eq + 1).trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double parseDouble(String s) {
|
||||||
|
if (s == null) return 0;
|
||||||
|
try {
|
||||||
|
return Double.parseDouble(s.trim());
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isLinux() {
|
||||||
|
return System.getProperty("os.name", "").toLowerCase(Locale.ROOT).startsWith("linux");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
import javax.swing.BorderFactory;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
@@ -52,7 +54,7 @@ public final class DropDownToggleButton extends JPanel {
|
|||||||
// width comes out of the toggle half instead of being added to it.
|
// width comes out of the toggle half instead of being added to it.
|
||||||
Dimension plain = button.getPreferredSize();
|
Dimension plain = button.getPreferredSize();
|
||||||
button.setPreferredSize(new Dimension(
|
button.setPreferredSize(new Dimension(
|
||||||
Math.max(icon.getIconWidth() + 8, plain.width - arrow.getPreferredSize().width),
|
Math.max(icon.getIconWidth() + UIScale.scale(8), plain.width - arrow.getPreferredSize().width),
|
||||||
plain.height));
|
plain.height));
|
||||||
|
|
||||||
add(button, BorderLayout.CENTER);
|
add(button, BorderLayout.CENTER);
|
||||||
@@ -109,12 +111,12 @@ public final class DropDownToggleButton extends JPanel {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getIconWidth() {
|
public int getIconWidth() {
|
||||||
return 7;
|
return UIScale.scale(7);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getIconHeight() {
|
public int getIconHeight() {
|
||||||
return 4;
|
return UIScale.scale(4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.net.ChannelNode;
|
import com.ts3client.net.ChannelNode;
|
||||||
import com.ts3client.net.ClientEntry;
|
import com.ts3client.net.ClientEntry;
|
||||||
import com.ts3client.net.ServerModel;
|
import com.ts3client.net.ServerModel;
|
||||||
@@ -160,14 +161,16 @@ final class DropIndicatorTree extends JTree {
|
|||||||
if (highlight != null || loc.getChildIndex() < 0) {
|
if (highlight != null || loc.getChildIndex() < 0) {
|
||||||
Rectangle r = getPathBounds(highlight != null ? highlight : loc.getPath());
|
Rectangle r = getPathBounds(highlight != null ? highlight : loc.getPath());
|
||||||
if (r != null) {
|
if (r != null) {
|
||||||
g2.setStroke(new BasicStroke(2f));
|
g2.setStroke(new BasicStroke(UIScale.scale(2f)));
|
||||||
g2.drawRoundRect(r.x, r.y + 1, r.width - 1, r.height - 3, 4, 4);
|
int arc = UIScale.scale(4);
|
||||||
|
g2.drawRoundRect(r.x, r.y + 1, r.width - 1, r.height - 3, arc, arc);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Rectangle line = insertLine(loc);
|
Rectangle line = insertLine(loc);
|
||||||
if (line != null) {
|
if (line != null) {
|
||||||
g2.fillRect(line.x, line.y - 1, line.width, 2);
|
int dot = UIScale.scale(7);
|
||||||
g2.fillOval(line.x - 3, line.y - 4, 7, 7);
|
g2.fillRect(line.x, line.y - line.height / 2, line.width, line.height);
|
||||||
|
g2.fillOval(line.x - dot / 2, line.y - dot / 2 - 1, dot, dot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
g2.dispose();
|
g2.dispose();
|
||||||
@@ -210,19 +213,19 @@ final class DropIndicatorTree extends JTree {
|
|||||||
return obj instanceof ChannelNode && Spacers.isSpacer(((ChannelNode) obj).name);
|
return obj instanceof ChannelNode && Spacers.isSpacer(((ChannelNode) obj).name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The 1px-tall strip where the insertion line goes, in tree coordinates. */
|
/** The strip where the insertion line goes, in tree coordinates. */
|
||||||
private Rectangle insertLine(JTree.DropLocation loc) {
|
private Rectangle insertLine(JTree.DropLocation loc) {
|
||||||
DefaultMutableTreeNode parent = (DefaultMutableTreeNode) loc.getPath().getLastPathComponent();
|
DefaultMutableTreeNode parent = (DefaultMutableTreeNode) loc.getPath().getLastPathComponent();
|
||||||
int index = loc.getChildIndex();
|
int index = loc.getChildIndex();
|
||||||
if (index < parent.getChildCount()) {
|
if (index < parent.getChildCount()) {
|
||||||
Rectangle r = getPathBounds(loc.getPath().pathByAddingChild(parent.getChildAt(index)));
|
Rectangle r = getPathBounds(loc.getPath().pathByAddingChild(parent.getChildAt(index)));
|
||||||
return r == null ? null : new Rectangle(r.x, r.y, getWidth() - r.x, 2);
|
return r == null ? null : new Rectangle(r.x, r.y, getWidth() - r.x, UIScale.scale(2));
|
||||||
}
|
}
|
||||||
if (parent.getChildCount() == 0) {
|
if (parent.getChildCount() == 0) {
|
||||||
Rectangle r = getPathBounds(loc.getPath());
|
Rectangle r = getPathBounds(loc.getPath());
|
||||||
if (r == null) return null;
|
if (r == null) return null;
|
||||||
int x = r.x + getRowHeight();
|
int x = r.x + getRowHeight();
|
||||||
return new Rectangle(x, r.y + r.height, getWidth() - x, 2);
|
return new Rectangle(x, r.y + r.height, getWidth() - x, UIScale.scale(2));
|
||||||
}
|
}
|
||||||
// Past the last child: below that child's whole (expanded) subtree.
|
// Past the last child: below that child's whole (expanded) subtree.
|
||||||
TreePath lastChild = loc.getPath().pathByAddingChild(parent.getChildAt(parent.getChildCount() - 1));
|
TreePath lastChild = loc.getPath().pathByAddingChild(parent.getChildAt(parent.getChildCount() - 1));
|
||||||
@@ -250,7 +253,7 @@ final class DropIndicatorTree extends JTree {
|
|||||||
*/
|
*/
|
||||||
private void paintBadges(Graphics g) {
|
private void paintBadges(Graphics g) {
|
||||||
Rectangle visible = getVisibleRect();
|
Rectangle visible = getVisibleRect();
|
||||||
int right = visible.x + visible.width - BADGE_MARGIN;
|
int right = visible.x + visible.width - UIScale.scale(BADGE_MARGIN);
|
||||||
for (int row = 0; row < getRowCount(); row++) {
|
for (int row = 0; row < getRowCount(); row++) {
|
||||||
Rectangle bounds = getRowBounds(row);
|
Rectangle bounds = getRowBounds(row);
|
||||||
if (bounds == null || bounds.y + bounds.height < visible.y) continue;
|
if (bounds == null || bounds.y + bounds.height < visible.y) continue;
|
||||||
@@ -262,7 +265,7 @@ final class DropIndicatorTree extends JTree {
|
|||||||
if (icons.isEmpty()) continue;
|
if (icons.isEmpty()) continue;
|
||||||
|
|
||||||
GroupIcons.Row strip = new GroupIcons.Row(icons);
|
GroupIcons.Row strip = new GroupIcons.Row(icons);
|
||||||
int x = Math.max(bounds.x + bounds.width + BADGE_GAP, right - strip.getIconWidth());
|
int x = Math.max(bounds.x + bounds.width + UIScale.scale(BADGE_GAP), right - strip.getIconWidth());
|
||||||
strip.paintIcon(this, g, x, bounds.y + (bounds.height - strip.getIconHeight()) / 2);
|
strip.paintIcon(this, g, x, bounds.y + (bounds.height - strip.getIconHeight()) / 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.ScaledEmptyBorder;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.net.ChannelNode;
|
import com.ts3client.net.ChannelNode;
|
||||||
import com.ts3client.net.TeamspeakConnection;
|
import com.ts3client.net.TeamspeakConnection;
|
||||||
import com.ts3client.net.filetransfer.FileTransfer;
|
import com.ts3client.net.filetransfer.FileTransfer;
|
||||||
@@ -65,15 +67,15 @@ public final class FileBrowserDialog extends JDialog {
|
|||||||
this.channelId = channel.id;
|
this.channelId = channel.id;
|
||||||
this.channelPassword = "";
|
this.channelPassword = "";
|
||||||
|
|
||||||
JPanel content = new JPanel(new BorderLayout(0, 8));
|
JPanel content = new JPanel(new BorderLayout(0, UIScale.scale(8)));
|
||||||
content.setBackground(Theme.windowBg());
|
content.setBackground(Theme.windowBg());
|
||||||
content.setBorder(BorderFactory.createEmptyBorder(10, 12, 10, 12));
|
content.setBorder(new ScaledEmptyBorder(10, 12, 10, 12));
|
||||||
content.add(buildToolbar(), BorderLayout.NORTH);
|
content.add(buildToolbar(), BorderLayout.NORTH);
|
||||||
content.add(buildTable(), BorderLayout.CENTER);
|
content.add(buildTable(), BorderLayout.CENTER);
|
||||||
content.add(buildTransfers(), BorderLayout.SOUTH);
|
content.add(buildTransfers(), BorderLayout.SOUTH);
|
||||||
setContentPane(content);
|
setContentPane(content);
|
||||||
|
|
||||||
setPreferredSize(new Dimension(640, 480));
|
setPreferredSize(UIScale.scale(new Dimension(640, 480)));
|
||||||
pack();
|
pack();
|
||||||
setLocationRelativeTo(owner);
|
setLocationRelativeTo(owner);
|
||||||
|
|
||||||
@@ -98,17 +100,17 @@ public final class FileBrowserDialog extends JDialog {
|
|||||||
deleteButton.addActionListener(e -> deleteSelected());
|
deleteButton.addActionListener(e -> deleteSelected());
|
||||||
|
|
||||||
bar.add(upButton);
|
bar.add(upButton);
|
||||||
bar.add(Box.createHorizontalStrut(6));
|
bar.add(Box.createHorizontalStrut(UIScale.scale(6)));
|
||||||
bar.add(refresh);
|
bar.add(refresh);
|
||||||
bar.add(Box.createHorizontalStrut(16));
|
bar.add(Box.createHorizontalStrut(UIScale.scale(16)));
|
||||||
bar.add(pathLabel);
|
bar.add(pathLabel);
|
||||||
bar.add(Box.createHorizontalGlue());
|
bar.add(Box.createHorizontalGlue());
|
||||||
bar.add(mkdir);
|
bar.add(mkdir);
|
||||||
bar.add(Box.createHorizontalStrut(6));
|
bar.add(Box.createHorizontalStrut(UIScale.scale(6)));
|
||||||
bar.add(upload);
|
bar.add(upload);
|
||||||
bar.add(Box.createHorizontalStrut(6));
|
bar.add(Box.createHorizontalStrut(UIScale.scale(6)));
|
||||||
bar.add(downloadButton);
|
bar.add(downloadButton);
|
||||||
bar.add(Box.createHorizontalStrut(6));
|
bar.add(Box.createHorizontalStrut(UIScale.scale(6)));
|
||||||
bar.add(deleteButton);
|
bar.add(deleteButton);
|
||||||
|
|
||||||
pathLabel.setFont(Theme.uiFont());
|
pathLabel.setFont(Theme.uiFont());
|
||||||
@@ -118,14 +120,14 @@ public final class FileBrowserDialog extends JDialog {
|
|||||||
|
|
||||||
private JScrollPane buildTable() {
|
private JScrollPane buildTable() {
|
||||||
table.setFont(Theme.uiFont());
|
table.setFont(Theme.uiFont());
|
||||||
table.setRowHeight(20);
|
table.setRowHeight(UIScale.scale(20));
|
||||||
table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
||||||
table.setFillsViewportHeight(true);
|
table.setFillsViewportHeight(true);
|
||||||
table.getColumnModel().getColumn(0).setPreferredWidth(300);
|
table.getColumnModel().getColumn(0).setPreferredWidth(UIScale.scale(300));
|
||||||
table.getColumnModel().getColumn(0).setCellRenderer(new NameRenderer());
|
table.getColumnModel().getColumn(0).setCellRenderer(new NameRenderer());
|
||||||
table.getColumnModel().getColumn(1).setPreferredWidth(90);
|
table.getColumnModel().getColumn(1).setPreferredWidth(UIScale.scale(90));
|
||||||
table.getColumnModel().getColumn(2).setPreferredWidth(70);
|
table.getColumnModel().getColumn(2).setPreferredWidth(UIScale.scale(70));
|
||||||
table.getColumnModel().getColumn(3).setPreferredWidth(150);
|
table.getColumnModel().getColumn(3).setPreferredWidth(UIScale.scale(150));
|
||||||
table.getSelectionModel().addListSelectionListener(e -> updateButtons());
|
table.getSelectionModel().addListSelectionListener(e -> updateButtons());
|
||||||
table.addMouseListener(new MouseAdapter() {
|
table.addMouseListener(new MouseAdapter() {
|
||||||
@Override
|
@Override
|
||||||
@@ -146,7 +148,7 @@ public final class FileBrowserDialog extends JDialog {
|
|||||||
transfersPanel.setBackground(Theme.windowBg());
|
transfersPanel.setBackground(Theme.windowBg());
|
||||||
JScrollPane scroll = new JScrollPane(transfersPanel);
|
JScrollPane scroll = new JScrollPane(transfersPanel);
|
||||||
scroll.setBorder(BorderFactory.createTitledBorder("Transfers"));
|
scroll.setBorder(BorderFactory.createTitledBorder("Transfers"));
|
||||||
scroll.setPreferredSize(new Dimension(10, 120));
|
scroll.setPreferredSize(UIScale.scale(new Dimension(10, 120)));
|
||||||
scroll.getViewport().setBackground(Theme.windowBg());
|
scroll.getViewport().setBackground(Theme.windowBg());
|
||||||
return scroll;
|
return scroll;
|
||||||
}
|
}
|
||||||
@@ -325,10 +327,10 @@ public final class FileBrowserDialog extends JDialog {
|
|||||||
private FileTransfer transfer;
|
private FileTransfer transfer;
|
||||||
|
|
||||||
TransferRow() {
|
TransferRow() {
|
||||||
setLayout(new BorderLayout(8, 0));
|
setLayout(new BorderLayout(UIScale.scale(8), 0));
|
||||||
setBackground(Theme.windowBg());
|
setBackground(Theme.windowBg());
|
||||||
setBorder(BorderFactory.createEmptyBorder(3, 2, 3, 2));
|
setBorder(new ScaledEmptyBorder(3, 2, 3, 2));
|
||||||
setMaximumSize(new Dimension(Integer.MAX_VALUE, 44));
|
setMaximumSize(new Dimension(Integer.MAX_VALUE, UIScale.scale(44)));
|
||||||
label.setFont(Theme.uiFont());
|
label.setFont(Theme.uiFont());
|
||||||
bar.setStringPainted(true);
|
bar.setStringPainted(true);
|
||||||
add(label, BorderLayout.NORTH);
|
add(label, BorderLayout.NORTH);
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
import com.formdev.flatlaf.util.ScaledEmptyBorder;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
@@ -27,7 +29,7 @@ class FormPanel extends JPanel implements Scrollable {
|
|||||||
|
|
||||||
FormPanel() {
|
FormPanel() {
|
||||||
super(new GridBagLayout());
|
super(new GridBagLayout());
|
||||||
setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));
|
setBorder(new ScaledEmptyBorder(12, 12, 12, 12));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -57,7 +59,7 @@ class FormPanel extends JPanel implements Scrollable {
|
|||||||
|
|
||||||
static GridBagConstraints gbc() {
|
static GridBagConstraints gbc() {
|
||||||
GridBagConstraints c = new GridBagConstraints();
|
GridBagConstraints c = new GridBagConstraints();
|
||||||
c.insets = new Insets(4, 4, 4, 4);
|
c.insets = UIScale.scale(new Insets(4, 4, 4, 4));
|
||||||
c.anchor = GridBagConstraints.WEST;
|
c.anchor = GridBagConstraints.WEST;
|
||||||
c.fill = GridBagConstraints.HORIZONTAL;
|
c.fill = GridBagConstraints.HORIZONTAL;
|
||||||
return c;
|
return c;
|
||||||
@@ -81,8 +83,8 @@ class FormPanel extends JPanel implements Scrollable {
|
|||||||
*/
|
*/
|
||||||
static void limitWidth(JComponent comp, int preferredWidth) {
|
static void limitWidth(JComponent comp, int preferredWidth) {
|
||||||
int height = comp.getPreferredSize().height;
|
int height = comp.getPreferredSize().height;
|
||||||
comp.setPreferredSize(new Dimension(preferredWidth, height));
|
comp.setPreferredSize(new Dimension(UIScale.scale(preferredWidth), height));
|
||||||
comp.setMinimumSize(new Dimension(MIN_FIELD_WIDTH, height));
|
comp.setMinimumSize(new Dimension(UIScale.scale(MIN_FIELD_WIDTH), height));
|
||||||
}
|
}
|
||||||
|
|
||||||
static JPanel sliderWithLabel(JSlider slider, JLabel valueLabel) {
|
static JPanel sliderWithLabel(JSlider slider, JLabel valueLabel) {
|
||||||
@@ -94,7 +96,7 @@ class FormPanel extends JPanel implements Scrollable {
|
|||||||
* the longest value so the slider does not jump around as it is dragged.
|
* the longest value so the slider does not jump around as it is dragged.
|
||||||
*/
|
*/
|
||||||
static JPanel sliderWithLabel(JSlider slider, JLabel valueLabel, int labelWidth) {
|
static JPanel sliderWithLabel(JSlider slider, JLabel valueLabel, int labelWidth) {
|
||||||
JPanel panel = new JPanel(new BorderLayout(6, 0));
|
JPanel panel = new JPanel(new BorderLayout(UIScale.scale(6), 0));
|
||||||
limitWidth(slider, SLIDER_WIDTH);
|
limitWidth(slider, SLIDER_WIDTH);
|
||||||
panel.add(slider, BorderLayout.CENTER);
|
panel.add(slider, BorderLayout.CENTER);
|
||||||
valueLabel.setPreferredSize(new Dimension(labelWidth, valueLabel.getPreferredSize().height));
|
valueLabel.setPreferredSize(new Dimension(labelWidth, valueLabel.getPreferredSize().height));
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.net.Group;
|
import com.ts3client.net.Group;
|
||||||
import com.ts3client.net.IconRepository;
|
import com.ts3client.net.IconRepository;
|
||||||
|
|
||||||
@@ -42,7 +43,7 @@ public final class GroupIcons {
|
|||||||
// its own style; only server-uploaded icons come off the wire. The theme keeps
|
// its own style; only server-uploaded icons come off the wire. The theme keeps
|
||||||
// its own cache, so those are not cached again here.
|
// its own cache, so those are not cached again here.
|
||||||
if (iconId > 0 && iconId <= IconRepository.MAX_BUNDLED_ID) {
|
if (iconId > 0 && iconId <= IconRepository.MAX_BUNDLED_ID) {
|
||||||
ImageIcon themed = IconTheme.icon("GROUP_" + iconId, SIZE);
|
ImageIcon themed = IconTheme.icon("GROUP_" + iconId, UIScale.scale(SIZE));
|
||||||
if (themed != null) return themed;
|
if (themed != null) return themed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,8 +55,9 @@ public final class GroupIcons {
|
|||||||
|
|
||||||
ImageIcon icon = new ImageIcon(data);
|
ImageIcon icon = new ImageIcon(data);
|
||||||
if (icon.getIconWidth() <= 0) return null;
|
if (icon.getIconWidth() <= 0) return null;
|
||||||
if (icon.getIconWidth() != SIZE || icon.getIconHeight() != SIZE) {
|
int size = UIScale.scale(SIZE);
|
||||||
icon = new ImageIcon(icon.getImage().getScaledInstance(SIZE, SIZE, Image.SCALE_SMOOTH));
|
if (icon.getIconWidth() != size || icon.getIconHeight() != size) {
|
||||||
|
icon = new ImageIcon(icon.getImage().getScaledInstance(size, size, Image.SCALE_SMOOTH));
|
||||||
}
|
}
|
||||||
decoded.put(iconId, icon);
|
decoded.put(iconId, icon);
|
||||||
return icon;
|
return icon;
|
||||||
@@ -89,15 +91,16 @@ public final class GroupIcons {
|
|||||||
int offset = x;
|
int offset = x;
|
||||||
for (Icon icon : icons) {
|
for (Icon icon : icons) {
|
||||||
icon.paintIcon(c, g, offset, y + (getIconHeight() - icon.getIconHeight()) / 2);
|
icon.paintIcon(c, g, offset, y + (getIconHeight() - icon.getIconHeight()) / 2);
|
||||||
offset += icon.getIconWidth() + GAP;
|
offset += icon.getIconWidth() + UIScale.scale(GAP);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getIconWidth() {
|
public int getIconWidth() {
|
||||||
int width = 0;
|
int width = 0;
|
||||||
for (Icon icon : icons) width += icon.getIconWidth() + GAP;
|
int gap = UIScale.scale(GAP);
|
||||||
return Math.max(0, width - GAP);
|
for (Icon icon : icons) width += icon.getIconWidth() + gap;
|
||||||
|
return Math.max(0, width - gap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.ScaledEmptyBorder;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.hotkey.Hotkey;
|
import com.ts3client.hotkey.Hotkey;
|
||||||
import com.ts3client.hotkey.HotkeyAction;
|
import com.ts3client.hotkey.HotkeyAction;
|
||||||
import com.ts3client.hotkey.HotkeyCombo;
|
import com.ts3client.hotkey.HotkeyCombo;
|
||||||
import com.ts3client.hotkey.HotkeyEngine;
|
import com.ts3client.hotkey.HotkeyEngine;
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
|
||||||
import javax.swing.Box;
|
import javax.swing.Box;
|
||||||
import javax.swing.DefaultListCellRenderer;
|
import javax.swing.DefaultListCellRenderer;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
@@ -120,8 +121,8 @@ final class HotkeyDialog extends JDialog {
|
|||||||
argumentField.setText(hotkey.argument == null ? "" : hotkey.argument);
|
argumentField.setText(hotkey.argument == null ? "" : hotkey.argument);
|
||||||
hint.setFont(hint.getFont().deriveFont(Font.ITALIC, hint.getFont().getSize2D() - 1f));
|
hint.setFont(hint.getFont().deriveFont(Font.ITALIC, hint.getFont().getSize2D() - 1f));
|
||||||
|
|
||||||
getContentPane().setLayout(new BorderLayout(8, 8));
|
getContentPane().setLayout(new BorderLayout(UIScale.scale(8), UIScale.scale(8)));
|
||||||
((JComponent) getContentPane()).setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
|
((JComponent) getContentPane()).setBorder(new ScaledEmptyBorder(8, 8, 8, 8));
|
||||||
getContentPane().add(buildActionPane(), BorderLayout.CENTER);
|
getContentPane().add(buildActionPane(), BorderLayout.CENTER);
|
||||||
getContentPane().add(buildForm(), BorderLayout.SOUTH);
|
getContentPane().add(buildForm(), BorderLayout.SOUTH);
|
||||||
|
|
||||||
@@ -129,12 +130,12 @@ final class HotkeyDialog extends JDialog {
|
|||||||
updateForAction();
|
updateForAction();
|
||||||
Dialogs.closeOnEscape(this, this::cancel);
|
Dialogs.closeOnEscape(this, this::cancel);
|
||||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||||
setSize(new Dimension(470, 560));
|
setSize(UIScale.scale(new Dimension(470, 560)));
|
||||||
setLocationRelativeTo(owner);
|
setLocationRelativeTo(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
private JPanel buildActionPane() {
|
private JPanel buildActionPane() {
|
||||||
JPanel p = new JPanel(new BorderLayout(0, 4));
|
JPanel p = new JPanel(new BorderLayout(0, UIScale.scale(4)));
|
||||||
p.add(new JLabel("Action:"), BorderLayout.NORTH);
|
p.add(new JLabel("Action:"), BorderLayout.NORTH);
|
||||||
p.add(new JScrollPane(tree), BorderLayout.CENTER);
|
p.add(new JScrollPane(tree), BorderLayout.CENTER);
|
||||||
p.add(advancedCheck, BorderLayout.SOUTH);
|
p.add(advancedCheck, BorderLayout.SOUTH);
|
||||||
@@ -144,7 +145,7 @@ final class HotkeyDialog extends JDialog {
|
|||||||
private JPanel buildForm() {
|
private JPanel buildForm() {
|
||||||
JPanel p = new JPanel(new GridBagLayout());
|
JPanel p = new JPanel(new GridBagLayout());
|
||||||
GridBagConstraints c = new GridBagConstraints();
|
GridBagConstraints c = new GridBagConstraints();
|
||||||
c.insets = new Insets(3, 3, 3, 3);
|
c.insets = UIScale.scale(new Insets(3, 3, 3, 3));
|
||||||
c.anchor = GridBagConstraints.WEST;
|
c.anchor = GridBagConstraints.WEST;
|
||||||
c.fill = GridBagConstraints.HORIZONTAL;
|
c.fill = GridBagConstraints.HORIZONTAL;
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.ScaledEmptyBorder;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.hotkey.Hotkey;
|
import com.ts3client.hotkey.Hotkey;
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
@@ -34,18 +35,18 @@ final class HotkeysPanel extends JPanel {
|
|||||||
private final JTable table = new JTable(model);
|
private final JTable table = new JTable(model);
|
||||||
|
|
||||||
HotkeysPanel(HotkeyService service) {
|
HotkeysPanel(HotkeyService service) {
|
||||||
super(new BorderLayout(6, 6));
|
super(new BorderLayout(UIScale.scale(6), UIScale.scale(6)));
|
||||||
this.service = service;
|
this.service = service;
|
||||||
setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
|
setBorder(new ScaledEmptyBorder(8, 8, 8, 8));
|
||||||
|
|
||||||
synchronized (service.all()) {
|
synchronized (service.all()) {
|
||||||
for (Hotkey h : service.all()) working.add(h.copy());
|
for (Hotkey h : service.all()) working.add(h.copy());
|
||||||
}
|
}
|
||||||
|
|
||||||
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||||
table.setRowHeight(table.getRowHeight() + 4);
|
table.setRowHeight(table.getRowHeight() + UIScale.scale(4));
|
||||||
table.getColumnModel().getColumn(0).setPreferredWidth(240);
|
table.getColumnModel().getColumn(0).setPreferredWidth(UIScale.scale(240));
|
||||||
table.getColumnModel().getColumn(1).setPreferredWidth(150);
|
table.getColumnModel().getColumn(1).setPreferredWidth(UIScale.scale(150));
|
||||||
table.addMouseListener(new MouseAdapter() {
|
table.addMouseListener(new MouseAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void mouseClicked(MouseEvent e) {
|
public void mouseClicked(MouseEvent e) {
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.text.BBCode;
|
import com.ts3client.text.BBCode;
|
||||||
|
|
||||||
import javax.swing.JEditorPane;
|
import javax.swing.JEditorPane;
|
||||||
import javax.swing.JViewport;
|
import javax.swing.JViewport;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.text.html.HTMLEditorKit;
|
import javax.swing.text.html.HTMLEditorKit;
|
||||||
|
import javax.swing.text.AttributeSet;
|
||||||
import javax.swing.text.html.StyleSheet;
|
import javax.swing.text.html.StyleSheet;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
|
import java.awt.Font;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The HTML panes showing TeamSpeak text — chat logs, client and channel information —
|
* The HTML panes showing TeamSpeak text — chat logs, client and channel information —
|
||||||
@@ -70,7 +73,7 @@ final class HtmlStyles {
|
|||||||
|
|
||||||
private static void installKit(JEditorPane pane, String html, boolean atEnd) {
|
private static void installKit(JEditorPane pane, String html, boolean atEnd) {
|
||||||
HTMLEditorKit kit = new HTMLEditorKit();
|
HTMLEditorKit kit = new HTMLEditorKit();
|
||||||
StyleSheet css = new StyleSheet();
|
StyleSheet css = new ScaledStyleSheet();
|
||||||
css.addStyleSheet(kit.getStyleSheet());
|
css.addStyleSheet(kit.getStyleSheet());
|
||||||
|
|
||||||
Object bodyStyle = pane.getClientProperty(BODY_STYLE);
|
Object bodyStyle = pane.getClientProperty(BODY_STYLE);
|
||||||
@@ -102,7 +105,24 @@ final class HtmlStyles {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** A CSS length of {@code px} at the interface scale. */
|
||||||
|
static String px(int px) {
|
||||||
|
return UIScale.scale(px) + "px";
|
||||||
|
}
|
||||||
|
|
||||||
private static String hex(Color c) {
|
private static String hex(Color c) {
|
||||||
return String.format("#%02x%02x%02x", c.getRed(), c.getGreen(), c.getBlue());
|
return String.format("#%02x%02x%02x", c.getRed(), c.getGreen(), c.getBlue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scales every font size, the way a browser scales CSS pixels: Swing takes a
|
||||||
|
* {@code font-size} in pixels literally, and the text is written in unscaled ones.
|
||||||
|
*/
|
||||||
|
private static final class ScaledStyleSheet extends StyleSheet {
|
||||||
|
@Override
|
||||||
|
public Font getFont(AttributeSet a) {
|
||||||
|
Font font = super.getFont(a);
|
||||||
|
return UIScale.getUserScaleFactor() == 1f ? font : font.deriveFont(UIScale.scale(font.getSize2D()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.ScaledEmptyBorder;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.net.IconRepository;
|
import com.ts3client.net.IconRepository;
|
||||||
import com.ts3client.net.TeamspeakConnection;
|
import com.ts3client.net.TeamspeakConnection;
|
||||||
|
|
||||||
@@ -69,7 +71,7 @@ final class IconChooserDialog extends JDialog {
|
|||||||
group("Local", localList, localFooter()));
|
group("Local", localList, localFooter()));
|
||||||
split.setResizeWeight(0.6);
|
split.setResizeWeight(0.6);
|
||||||
|
|
||||||
status.setBorder(BorderFactory.createEmptyBorder(2, 8, 2, 8));
|
status.setBorder(new ScaledEmptyBorder(2, 8, 2, 8));
|
||||||
status.setForeground(Theme.chatSystem());
|
status.setForeground(Theme.chatSystem());
|
||||||
|
|
||||||
getContentPane().setLayout(new BorderLayout());
|
getContentPane().setLayout(new BorderLayout());
|
||||||
@@ -81,7 +83,7 @@ final class IconChooserDialog extends JDialog {
|
|||||||
|
|
||||||
Dialogs.closeOnEscape(this);
|
Dialogs.closeOnEscape(this);
|
||||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||||
setSize(new Dimension(520, 360));
|
setSize(UIScale.scale(new Dimension(520, 360)));
|
||||||
setLocationRelativeTo(owner);
|
setLocationRelativeTo(owner);
|
||||||
|
|
||||||
loadRemoteIcons();
|
loadRemoteIcons();
|
||||||
@@ -101,8 +103,8 @@ final class IconChooserDialog extends JDialog {
|
|||||||
list.setLayoutOrientation(JList.HORIZONTAL_WRAP);
|
list.setLayoutOrientation(JList.HORIZONTAL_WRAP);
|
||||||
list.setVisibleRowCount(-1);
|
list.setVisibleRowCount(-1);
|
||||||
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||||
list.setFixedCellWidth(32);
|
list.setFixedCellWidth(UIScale.scale(32));
|
||||||
list.setFixedCellHeight(32);
|
list.setFixedCellHeight(UIScale.scale(32));
|
||||||
list.setCellRenderer(new IconCellRenderer());
|
list.setCellRenderer(new IconCellRenderer());
|
||||||
list.addListSelectionListener(e -> onSelectionChanged(list));
|
list.addListSelectionListener(e -> onSelectionChanged(list));
|
||||||
list.addMouseListener(new MouseAdapter() {
|
list.addMouseListener(new MouseAdapter() {
|
||||||
@@ -129,7 +131,7 @@ final class IconChooserDialog extends JDialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private JPanel group(String title, JList<Long> list, Component footer) {
|
private JPanel group(String title, JList<Long> list, Component footer) {
|
||||||
JPanel panel = new JPanel(new BorderLayout(0, 4));
|
JPanel panel = new JPanel(new BorderLayout(0, UIScale.scale(4)));
|
||||||
panel.setBorder(BorderFactory.createTitledBorder(title));
|
panel.setBorder(BorderFactory.createTitledBorder(title));
|
||||||
panel.add(new JScrollPane(list), BorderLayout.CENTER);
|
panel.add(new JScrollPane(list), BorderLayout.CENTER);
|
||||||
panel.add(footer, BorderLayout.SOUTH);
|
panel.add(footer, BorderLayout.SOUTH);
|
||||||
@@ -150,7 +152,7 @@ final class IconChooserDialog extends JDialog {
|
|||||||
private Component localFooter() {
|
private Component localFooter() {
|
||||||
com.ts3client.gfx.IconPack active = IconTheme.get().activePack();
|
com.ts3client.gfx.IconPack active = IconTheme.get().activePack();
|
||||||
JLabel pack = new JLabel("Icon Pack: " + (active == null ? "none" : active.name()));
|
JLabel pack = new JLabel("Icon Pack: " + (active == null ? "none" : active.name()));
|
||||||
pack.setBorder(BorderFactory.createEmptyBorder(0, 4, 0, 4));
|
pack.setBorder(new ScaledEmptyBorder(0, 4, 0, 4));
|
||||||
return pack;
|
return pack;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.github.weisj.jsvg.SVGDocument;
|
import com.github.weisj.jsvg.SVGDocument;
|
||||||
import com.github.weisj.jsvg.parser.LoaderContext;
|
import com.github.weisj.jsvg.parser.LoaderContext;
|
||||||
import com.github.weisj.jsvg.parser.SVGLoader;
|
import com.github.weisj.jsvg.parser.SVGLoader;
|
||||||
@@ -33,6 +34,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
|||||||
*
|
*
|
||||||
* <p>Rendering is cached per key and size: vector art is rasterised at the size it
|
* <p>Rendering is cached per key and size: vector art is rasterised at the size it
|
||||||
* is asked for, bitmaps are scaled from the closest resolution the pack ships.
|
* is asked for, bitmaps are scaled from the closest resolution the pack ships.
|
||||||
|
* {@link #SIZE} and {@link #TOOLBAR_SIZE} are unscaled; sizes passed in are pixels.
|
||||||
*/
|
*/
|
||||||
public final class IconTheme {
|
public final class IconTheme {
|
||||||
|
|
||||||
@@ -86,12 +88,12 @@ public final class IconTheme {
|
|||||||
listeners.add(listener);
|
listeners.add(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return the icon for a TeamSpeak icon key at 16×16, or {@code null} if no pack has it */
|
/** @return the icon for a TeamSpeak icon key at tree-row size, or {@code null} if no pack has it */
|
||||||
public static ImageIcon icon(String key) {
|
public static ImageIcon icon(String key) {
|
||||||
return icon(key, SIZE);
|
return icon(key, UIScale.scale(SIZE));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return the icon for a TeamSpeak icon key, or {@code null} if no pack has it */
|
/** @return the icon for a TeamSpeak icon key at {@code size} pixels, or {@code null} if no pack has it */
|
||||||
public static ImageIcon icon(String key, int size) {
|
public static ImageIcon icon(String key, int size) {
|
||||||
return INSTANCE.lookup(key, size);
|
return INSTANCE.lookup(key, size);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
import java.awt.BasicStroke;
|
import java.awt.BasicStroke;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
@@ -15,14 +17,25 @@ import java.awt.image.BufferedImage;
|
|||||||
* so a machine with no icon pack installed still gets a complete (if plain) set.
|
* so a machine with no icon pack installed still gets a complete (if plain) set.
|
||||||
* Everything else — menu and dialog decoration — simply stays blank without a pack,
|
* Everything else — menu and dialog decoration — simply stays blank without a pack,
|
||||||
* which is why {@link #of(String)} may return {@code null}.
|
* which is why {@link #of(String)} may return {@code null}.
|
||||||
|
*
|
||||||
|
* <p>Icons come at the interface's scale; the variants taking a size take it in pixels.
|
||||||
*/
|
*/
|
||||||
public final class Icons {
|
public final class Icons {
|
||||||
|
|
||||||
|
/** The box the built-in painters draw in. */
|
||||||
private static final int SZ = IconTheme.SIZE;
|
private static final int SZ = IconTheme.SIZE;
|
||||||
|
|
||||||
private Icons() {
|
private Icons() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int small() {
|
||||||
|
return UIScale.scale(IconTheme.SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int large() {
|
||||||
|
return UIScale.scale(IconTheme.TOOLBAR_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
private interface Painter {
|
private interface Painter {
|
||||||
void paint(Graphics2D g);
|
void paint(Graphics2D g);
|
||||||
}
|
}
|
||||||
@@ -44,13 +57,13 @@ public final class Icons {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return the pack's icon at a given size, or {@code null} when it has none */
|
/** @return the pack's icon at {@code size} pixels, or {@code null} when it has none */
|
||||||
public static ImageIcon of(String key, int size) {
|
public static ImageIcon of(String key, int size) {
|
||||||
return IconTheme.icon(key, size);
|
return IconTheme.icon(key, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ImageIcon themed(String key, Painter fallback) {
|
private static ImageIcon themed(String key, Painter fallback) {
|
||||||
return themed(key, SZ, fallback);
|
return themed(key, small(), fallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ImageIcon themed(String key, int size, Painter fallback) {
|
private static ImageIcon themed(String key, int size, Painter fallback) {
|
||||||
@@ -64,14 +77,14 @@ public final class Icons {
|
|||||||
*/
|
*/
|
||||||
private static ImageIcon themed(String[] keys, Painter fallback) {
|
private static ImageIcon themed(String[] keys, Painter fallback) {
|
||||||
for (String key : keys) {
|
for (String key : keys) {
|
||||||
ImageIcon icon = IconTheme.icon(key, SZ);
|
ImageIcon icon = IconTheme.icon(key, small());
|
||||||
if (icon != null) return icon;
|
if (icon != null) return icon;
|
||||||
}
|
}
|
||||||
return make(fallback);
|
return make(fallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ImageIcon make(Painter p) {
|
private static ImageIcon make(Painter p) {
|
||||||
return make(SZ, p);
|
return make(small(), p);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Runs a painter (which draws in a 16×16 box) scaled to {@code size}. */
|
/** Runs a painter (which draws in a 16×16 box) scaled to {@code size}. */
|
||||||
@@ -129,7 +142,7 @@ public final class Icons {
|
|||||||
// ---- client status icons ----
|
// ---- client status icons ----
|
||||||
|
|
||||||
public static ImageIcon clientIdle() {
|
public static ImageIcon clientIdle() {
|
||||||
return clientIdle(SZ);
|
return clientIdle(small());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ImageIcon clientIdle(int size) {
|
public static ImageIcon clientIdle(int size) {
|
||||||
@@ -137,7 +150,7 @@ public final class Icons {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static ImageIcon clientTalking() {
|
public static ImageIcon clientTalking() {
|
||||||
return clientTalking(SZ);
|
return clientTalking(small());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ImageIcon clientTalking(int size) {
|
public static ImageIcon clientTalking(int size) {
|
||||||
@@ -145,7 +158,7 @@ public final class Icons {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static ImageIcon clientAway() {
|
public static ImageIcon clientAway() {
|
||||||
return clientAway(SZ);
|
return clientAway(small());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ImageIcon clientAway(int size) {
|
public static ImageIcon clientAway(int size) {
|
||||||
@@ -154,12 +167,12 @@ public final class Icons {
|
|||||||
|
|
||||||
/** A channel commander that is not talking. */
|
/** A channel commander that is not talking. */
|
||||||
public static ImageIcon clientCommander() {
|
public static ImageIcon clientCommander() {
|
||||||
return clientCommander(SZ);
|
return clientCommander(small());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A channel commander that is talking. */
|
/** A channel commander that is talking. */
|
||||||
public static ImageIcon clientCommanderTalking() {
|
public static ImageIcon clientCommanderTalking() {
|
||||||
return clientCommanderTalking(SZ);
|
return clientCommanderTalking(small());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ImageIcon clientQuery() {
|
public static ImageIcon clientQuery() {
|
||||||
@@ -167,7 +180,7 @@ public final class Icons {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static ImageIcon micMuted() {
|
public static ImageIcon micMuted() {
|
||||||
return micMuted(SZ);
|
return micMuted(small());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ImageIcon micMuted(int size) {
|
public static ImageIcon micMuted(int size) {
|
||||||
@@ -175,7 +188,7 @@ public final class Icons {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static ImageIcon speakerMuted() {
|
public static ImageIcon speakerMuted() {
|
||||||
return speakerMuted(SZ);
|
return speakerMuted(small());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ImageIcon speakerMuted(int size) {
|
public static ImageIcon speakerMuted(int size) {
|
||||||
@@ -184,7 +197,7 @@ public final class Icons {
|
|||||||
|
|
||||||
/** The capture device is unavailable (another tab holds it), as opposed to muted. */
|
/** The capture device is unavailable (another tab holds it), as opposed to muted. */
|
||||||
public static ImageIcon micDisabled() {
|
public static ImageIcon micDisabled() {
|
||||||
return micDisabled(SZ);
|
return micDisabled(small());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ImageIcon micDisabled(int size) {
|
public static ImageIcon micDisabled(int size) {
|
||||||
@@ -193,7 +206,7 @@ public final class Icons {
|
|||||||
|
|
||||||
/** The playback device is unavailable, as opposed to muted/deafened. */
|
/** The playback device is unavailable, as opposed to muted/deafened. */
|
||||||
public static ImageIcon speakerDisabled() {
|
public static ImageIcon speakerDisabled() {
|
||||||
return speakerDisabled(SZ);
|
return speakerDisabled(small());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ImageIcon speakerDisabled(int size) {
|
public static ImageIcon speakerDisabled(int size) {
|
||||||
@@ -202,7 +215,7 @@ public final class Icons {
|
|||||||
|
|
||||||
/** TS3's "Local Mic Mute": silenced, but not reported as muted. */
|
/** TS3's "Local Mic Mute": silenced, but not reported as muted. */
|
||||||
public static ImageIcon micLocalMuted() {
|
public static ImageIcon micLocalMuted() {
|
||||||
return micLocalMuted(SZ);
|
return micLocalMuted(small());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ImageIcon micLocalMuted(int size) {
|
public static ImageIcon micLocalMuted(int size) {
|
||||||
@@ -222,32 +235,32 @@ public final class Icons {
|
|||||||
// ---- toolbar / action icons ----
|
// ---- toolbar / action icons ----
|
||||||
|
|
||||||
public static ImageIcon connect() {
|
public static ImageIcon connect() {
|
||||||
return themed("CONNECT", IconTheme.TOOLBAR_SIZE, Icons::paintConnect);
|
return themed("CONNECT", large(), Icons::paintConnect);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ImageIcon disconnect() {
|
public static ImageIcon disconnect() {
|
||||||
return themed("DISCONNECT", IconTheme.TOOLBAR_SIZE, Icons::paintDisconnect);
|
return themed("DISCONNECT", large(), Icons::paintDisconnect);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ImageIcon mic() {
|
public static ImageIcon mic() {
|
||||||
return themed("CAPTURE", IconTheme.TOOLBAR_SIZE, Icons::paintMic);
|
return themed("CAPTURE", large(), Icons::paintMic);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ImageIcon speaker() {
|
public static ImageIcon speaker() {
|
||||||
return themed("PLAYBACK", IconTheme.TOOLBAR_SIZE, Icons::paintSpeaker);
|
return themed("PLAYBACK", large(), Icons::paintSpeaker);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ImageIcon micMutedLarge() {
|
public static ImageIcon micMutedLarge() {
|
||||||
return themed("INPUT_MUTED", IconTheme.TOOLBAR_SIZE, Icons::paintMicMuted);
|
return themed("INPUT_MUTED", large(), Icons::paintMicMuted);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ImageIcon speakerMutedLarge() {
|
public static ImageIcon speakerMutedLarge() {
|
||||||
return themed("OUTPUT_MUTED", IconTheme.TOOLBAR_SIZE, Icons::paintSpeakerMuted);
|
return themed("OUTPUT_MUTED", large(), Icons::paintSpeakerMuted);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Marks the server that currently owns the microphone. */
|
/** Marks the server that currently owns the microphone. */
|
||||||
public static ImageIcon micActive() {
|
public static ImageIcon micActive() {
|
||||||
return themed("ACTIVATE_MICROPHONE", IconTheme.TOOLBAR_SIZE, Icons::paintMicActive);
|
return themed("ACTIVATE_MICROPHONE", large(), Icons::paintMicActive);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The same marker at tab-label size. */
|
/** The same marker at tab-label size. */
|
||||||
@@ -262,7 +275,7 @@ public final class Icons {
|
|||||||
public static ImageIcon channelClientPair() {
|
public static ImageIcon channelClientPair() {
|
||||||
ImageIcon channel = channel(true);
|
ImageIcon channel = channel(true);
|
||||||
ImageIcon edit = themed("EDIT", Icons::paintEdit);
|
ImageIcon edit = themed("EDIT", Icons::paintEdit);
|
||||||
int gap = 8;
|
int gap = UIScale.scale(8);
|
||||||
int width = channel.getIconWidth() + gap + edit.getIconWidth();
|
int width = channel.getIconWidth() + gap + edit.getIconWidth();
|
||||||
int height = Math.max(channel.getIconHeight(), edit.getIconHeight());
|
int height = Math.max(channel.getIconHeight(), edit.getIconHeight());
|
||||||
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
|
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
|
||||||
@@ -282,15 +295,15 @@ public final class Icons {
|
|||||||
|
|
||||||
/** The toolbar's away marker. */
|
/** The toolbar's away marker. */
|
||||||
public static ImageIcon away() {
|
public static ImageIcon away() {
|
||||||
return themed("AWAY", IconTheme.TOOLBAR_SIZE, g -> paintPerson(g, Theme.away()));
|
return themed("AWAY", large(), g -> paintPerson(g, Theme.away()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ImageIcon settings() {
|
public static ImageIcon settings() {
|
||||||
return themed("SETTINGS", IconTheme.TOOLBAR_SIZE, Icons::paintSettings);
|
return themed("SETTINGS", large(), Icons::paintSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ImageIcon app() {
|
public static ImageIcon app() {
|
||||||
return app(SZ);
|
return app(small());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ImageIcon app(int size) {
|
public static ImageIcon app(int size) {
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.ScaledEmptyBorder;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.config.Bookmarks;
|
import com.ts3client.config.Bookmarks;
|
||||||
import com.ts3client.config.IdentityEntry;
|
import com.ts3client.config.IdentityEntry;
|
||||||
import com.ts3client.config.IdentityStore;
|
import com.ts3client.config.IdentityStore;
|
||||||
@@ -7,7 +9,6 @@ import com.ts3client.config.Settings;
|
|||||||
import com.ts3client.teamspeak.TeamSpeakImporter;
|
import com.ts3client.teamspeak.TeamSpeakImporter;
|
||||||
import com.ts3client.teamspeak.TeamSpeakSettingsDb;
|
import com.ts3client.teamspeak.TeamSpeakSettingsDb;
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
|
||||||
import javax.swing.Box;
|
import javax.swing.Box;
|
||||||
import javax.swing.BoxLayout;
|
import javax.swing.BoxLayout;
|
||||||
import javax.swing.DefaultListCellRenderer;
|
import javax.swing.DefaultListCellRenderer;
|
||||||
@@ -73,12 +74,12 @@ public final class IdentitiesDialog extends JDialog {
|
|||||||
list.setVisibleRowCount(10);
|
list.setVisibleRowCount(10);
|
||||||
|
|
||||||
JScrollPane scroll = new JScrollPane(list);
|
JScrollPane scroll = new JScrollPane(list);
|
||||||
scroll.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
|
scroll.setBorder(new ScaledEmptyBorder(8, 8, 8, 8));
|
||||||
scroll.setPreferredSize(new Dimension(220, 260));
|
scroll.setPreferredSize(UIScale.scale(new Dimension(220, 260)));
|
||||||
|
|
||||||
JPanel buttons = new JPanel();
|
JPanel buttons = new JPanel();
|
||||||
buttons.setLayout(new BoxLayout(buttons, BoxLayout.Y_AXIS));
|
buttons.setLayout(new BoxLayout(buttons, BoxLayout.Y_AXIS));
|
||||||
buttons.setBorder(BorderFactory.createEmptyBorder(8, 0, 8, 8));
|
buttons.setBorder(new ScaledEmptyBorder(8, 0, 8, 8));
|
||||||
addButton(buttons, "New…", this::newIdentity);
|
addButton(buttons, "New…", this::newIdentity);
|
||||||
addButton(buttons, "Import…", this::importIdentity);
|
addButton(buttons, "Import…", this::importIdentity);
|
||||||
if (TeamSpeakSettingsDb.locate() != null) {
|
if (TeamSpeakSettingsDb.locate() != null) {
|
||||||
@@ -103,15 +104,15 @@ public final class IdentitiesDialog extends JDialog {
|
|||||||
Dialogs.closeOnEscape(this);
|
Dialogs.closeOnEscape(this);
|
||||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||||
pack();
|
pack();
|
||||||
setMinimumSize(new Dimension(640, 320));
|
setMinimumSize(UIScale.scale(new Dimension(640, 320)));
|
||||||
setLocationRelativeTo(owner);
|
setLocationRelativeTo(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
private JPanel buildDetails() {
|
private JPanel buildDetails() {
|
||||||
JPanel form = new JPanel(new GridBagLayout());
|
JPanel form = new JPanel(new GridBagLayout());
|
||||||
form.setBorder(BorderFactory.createEmptyBorder(12, 4, 12, 8));
|
form.setBorder(new ScaledEmptyBorder(12, 4, 12, 8));
|
||||||
GridBagConstraints c = new GridBagConstraints();
|
GridBagConstraints c = new GridBagConstraints();
|
||||||
c.insets = new Insets(4, 4, 4, 4);
|
c.insets = UIScale.scale(new Insets(4, 4, 4, 4));
|
||||||
c.anchor = GridBagConstraints.NORTHWEST;
|
c.anchor = GridBagConstraints.NORTHWEST;
|
||||||
c.fill = GridBagConstraints.HORIZONTAL;
|
c.fill = GridBagConstraints.HORIZONTAL;
|
||||||
|
|
||||||
@@ -149,7 +150,7 @@ public final class IdentitiesDialog extends JDialog {
|
|||||||
b.setMaximumSize(new Dimension(Integer.MAX_VALUE, b.getPreferredSize().height));
|
b.setMaximumSize(new Dimension(Integer.MAX_VALUE, b.getPreferredSize().height));
|
||||||
b.addActionListener(e -> action.run());
|
b.addActionListener(e -> action.run());
|
||||||
panel.add(b);
|
panel.add(b);
|
||||||
panel.add(Box.createVerticalStrut(4));
|
panel.add(Box.createVerticalStrut(UIScale.scale(4)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String label(IdentityEntry e) {
|
private String label(IdentityEntry e) {
|
||||||
@@ -335,8 +336,8 @@ public final class IdentitiesDialog extends JDialog {
|
|||||||
|
|
||||||
JDialog progress = new JDialog(this, "Please wait", true);
|
JDialog progress = new JDialog(this, "Please wait", true);
|
||||||
JLabel status = new JLabel(message);
|
JLabel status = new JLabel(message);
|
||||||
JPanel panel = new JPanel(new BorderLayout(8, 8));
|
JPanel panel = new JPanel(new BorderLayout(UIScale.scale(8), UIScale.scale(8)));
|
||||||
panel.setBorder(BorderFactory.createEmptyBorder(16, 16, 12, 16));
|
panel.setBorder(new ScaledEmptyBorder(16, 16, 12, 16));
|
||||||
panel.add(status, BorderLayout.CENTER);
|
panel.add(status, BorderLayout.CENTER);
|
||||||
if (cancellable) {
|
if (cancellable) {
|
||||||
JButton cancel = new JButton("Stop");
|
JButton cancel = new JButton("Stop");
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.ScaledEmptyBorder;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.contacts.Contact;
|
import com.ts3client.contacts.Contact;
|
||||||
import com.ts3client.net.ChannelNode;
|
import com.ts3client.net.ChannelNode;
|
||||||
import com.ts3client.net.ClientEntry;
|
import com.ts3client.net.ClientEntry;
|
||||||
@@ -65,7 +67,7 @@ public final class InfoPanel extends JPanel {
|
|||||||
|
|
||||||
public InfoPanel() {
|
public InfoPanel() {
|
||||||
super(new BorderLayout());
|
super(new BorderLayout());
|
||||||
pane.setBorder(BorderFactory.createEmptyBorder(6, 8, 6, 8));
|
pane.setBorder(new ScaledEmptyBorder(6, 8, 6, 8));
|
||||||
pane.addHyperlinkListener(e -> {
|
pane.addHyperlinkListener(e -> {
|
||||||
if (e.getEventType() != javax.swing.event.HyperlinkEvent.EventType.ACTIVATED) return;
|
if (e.getEventType() != javax.swing.event.HyperlinkEvent.EventType.ACTIVATED) return;
|
||||||
Links.open(e.getDescription(), this);
|
Links.open(e.getDescription(), this);
|
||||||
@@ -82,7 +84,7 @@ public final class InfoPanel extends JPanel {
|
|||||||
toggle.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
toggle.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
||||||
toggle.setToolTipText("Show these details in a chat tab instead");
|
toggle.setToolTipText("Show these details in a chat tab instead");
|
||||||
toggle.setOpaque(true);
|
toggle.setOpaque(true);
|
||||||
toggle.setBorder(BorderFactory.createEmptyBorder(1, 4, 1, 4));
|
toggle.setBorder(new ScaledEmptyBorder(1, 4, 1, 4));
|
||||||
toggle.addMouseListener(new MouseAdapter() {
|
toggle.addMouseListener(new MouseAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void mousePressed(MouseEvent e) {
|
public void mousePressed(MouseEvent e) {
|
||||||
@@ -188,7 +190,7 @@ public final class InfoPanel extends JPanel {
|
|||||||
private String body() {
|
private String body() {
|
||||||
if (shownChannel != null) return channelBody(shownChannel);
|
if (shownChannel != null) return channelBody(shownChannel);
|
||||||
if (shownClient != null) {
|
if (shownClient != null) {
|
||||||
renderedAvatarBox = inChatTab ? AvatarHtml.MAX_SIZE : avatarBox();
|
renderedAvatarBox = inChatTab ? UIScale.scale(AvatarHtml.MAX_SIZE) : avatarBox();
|
||||||
return clientBody(shownClient, shownModel, shownIcons, shownContact, shownAvatar, renderedAvatarBox);
|
return clientBody(shownClient, shownModel, shownIcons, shownContact, shownAvatar, renderedAvatarBox);
|
||||||
}
|
}
|
||||||
return "<i class='muted'>Select a channel or client to see details.</i>";
|
return "<i class='muted'>Select a channel or client to see details.</i>";
|
||||||
@@ -201,9 +203,11 @@ public final class InfoPanel extends JPanel {
|
|||||||
*/
|
*/
|
||||||
private int avatarBox() {
|
private int avatarBox() {
|
||||||
int width = scroll.getViewport().getWidth();
|
int width = scroll.getViewport().getWidth();
|
||||||
if (width <= 0) return AvatarHtml.MAX_SIZE;
|
int max = UIScale.scale(AvatarHtml.MAX_SIZE);
|
||||||
int box = width * 2 / 5 / AVATAR_BOX_STEP * AVATAR_BOX_STEP;
|
if (width <= 0) return max;
|
||||||
return Math.max(AVATAR_MIN_SIZE, Math.min(AvatarHtml.MAX_SIZE, box));
|
int step = UIScale.scale(AVATAR_BOX_STEP);
|
||||||
|
int box = width * 2 / 5 / step * step;
|
||||||
|
return Math.max(UIScale.scale(AVATAR_MIN_SIZE), Math.min(max, box));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String channelBody(ChannelNode ch) {
|
private static String channelBody(ChannelNode ch) {
|
||||||
@@ -231,7 +235,7 @@ public final class InfoPanel extends JPanel {
|
|||||||
// The avatar sits top-right beside the details, like the official client's info frame.
|
// The avatar sits top-right beside the details, like the official client's info frame.
|
||||||
return "<table width='100%' cellpadding='0' cellspacing='0'><tr>"
|
return "<table width='100%' cellpadding='0' cellspacing='0'><tr>"
|
||||||
+ "<td valign='top'>" + details + "</td>"
|
+ "<td valign='top'>" + details + "</td>"
|
||||||
+ "<td valign='top' align='right' style='padding-left:8px'>" + picture + "</td>"
|
+ "<td valign='top' align='right' style='padding-left:" + HtmlStyles.px(8) + "'>" + picture + "</td>"
|
||||||
+ "</tr></table>";
|
+ "</tr></table>";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,11 +301,11 @@ public final class InfoPanel extends JPanel {
|
|||||||
if (group.iconId == 0) return "";
|
if (group.iconId == 0) return "";
|
||||||
File file = icons.file(group.iconId);
|
File file = icons.file(group.iconId);
|
||||||
if (file == null) return "";
|
if (file == null) return "";
|
||||||
return "<img src='" + file.toURI() + "' width='14' height='14'> ";
|
return "<img src='" + file.toURI() + "' width='" + UIScale.scale(14) + "' height='" + UIScale.scale(14) + "'> ";
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String heading(String text) {
|
private static String heading(String text) {
|
||||||
return "<div style='font-weight:bold;font-size:13px;margin-bottom:4px'>" + text + "</div>";
|
return "<div style='font-weight:bold;font-size:13px;margin-bottom:" + HtmlStyles.px(4) + "'>" + text + "</div>";
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void row(StringBuilder sb, String label, String value) {
|
private static void row(StringBuilder sb, String label, String value) {
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
@@ -25,11 +27,11 @@ public final class LevelMeter extends JComponent {
|
|||||||
private volatile boolean transmitting;
|
private volatile boolean transmitting;
|
||||||
|
|
||||||
public LevelMeter() {
|
public LevelMeter() {
|
||||||
setPreferredSize(new Dimension(240, 18));
|
setPreferredSize(UIScale.scale(new Dimension(240, 18)));
|
||||||
// A bare JComponent reports no minimum of its own, so a layout tight on space
|
// A bare JComponent reports no minimum of its own, so a layout tight on space
|
||||||
// collapses the bar to nothing. Keep the height and let only the width give.
|
// collapses the bar to nothing. Keep the height and let only the width give.
|
||||||
setMinimumSize(new Dimension(60, 18));
|
setMinimumSize(UIScale.scale(new Dimension(60, 18)));
|
||||||
setMaximumSize(new Dimension(Integer.MAX_VALUE, 18));
|
setMaximumSize(new Dimension(Integer.MAX_VALUE, UIScale.scale(18)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLevel(double db) {
|
public void setLevel(double db) {
|
||||||
@@ -70,16 +72,17 @@ public final class LevelMeter extends JComponent {
|
|||||||
int h = getHeight();
|
int h = getHeight();
|
||||||
|
|
||||||
g.setColor(new Color(0x2B2B2B));
|
g.setColor(new Color(0x2B2B2B));
|
||||||
g.fillRoundRect(0, 0, w - 1, h - 1, 6, 6);
|
int arc = UIScale.scale(6);
|
||||||
|
g.fillRoundRect(0, 0, w - 1, h - 1, arc, arc);
|
||||||
|
|
||||||
int level = dbToX(levelDb, w - 2);
|
int level = dbToX(levelDb, w - 2);
|
||||||
g.setColor(transmitting ? Theme.talking() : new Color(0x5A9BD4));
|
g.setColor(transmitting ? Theme.talking() : new Color(0x5A9BD4));
|
||||||
g.fillRoundRect(1, 1, Math.max(0, level), h - 3, 5, 5);
|
g.fillRoundRect(1, 1, Math.max(0, level), h - 3, arc - 1, arc - 1);
|
||||||
|
|
||||||
if (showThreshold) {
|
if (showThreshold) {
|
||||||
int tx = dbToX(thresholdDb, w - 2);
|
int tx = dbToX(thresholdDb, w - 2);
|
||||||
g.setColor(new Color(0xF0C419));
|
g.setColor(new Color(0xF0C419));
|
||||||
g.fillRect(tx, 1, 2, h - 3);
|
g.fillRect(tx, 1, UIScale.scale(2), h - 3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.audio.AudioBackend;
|
import com.ts3client.audio.AudioBackend;
|
||||||
import com.ts3client.audio.desktop.DesktopAudioBackend;
|
import com.ts3client.audio.desktop.DesktopAudioBackend;
|
||||||
import com.ts3client.config.AwayMessages;
|
import com.ts3client.config.AwayMessages;
|
||||||
@@ -14,7 +15,6 @@ import com.ts3client.net.ChannelNode;
|
|||||||
import com.ts3client.sound.SoundNotifier;
|
import com.ts3client.sound.SoundNotifier;
|
||||||
import com.ts3client.sound.SoundPlayer;
|
import com.ts3client.sound.SoundPlayer;
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
|
||||||
import javax.swing.JCheckBox;
|
import javax.swing.JCheckBox;
|
||||||
import javax.swing.JCheckBoxMenuItem;
|
import javax.swing.JCheckBoxMenuItem;
|
||||||
import javax.swing.JFrame;
|
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.
|
// Catches Ctrl+C / SIGTERM so we still leave the servers cleanly.
|
||||||
Runtime.getRuntime().addShutdownHook(shutdownHook);
|
Runtime.getRuntime().addShutdownHook(shutdownHook);
|
||||||
setMinimumSize(new Dimension(720, 480));
|
setMinimumSize(UIScale.scale(new Dimension(720, 480)));
|
||||||
|
|
||||||
tray = new TrayController(this, this::quit);
|
tray = new TrayController(this, this::quit);
|
||||||
menuBar = buildMenuBar();
|
menuBar = buildMenuBar();
|
||||||
@@ -128,7 +128,7 @@ public final class MainFrame extends JFrame implements ServerTabPane.Listener {
|
|||||||
|
|
||||||
statusTimer = new javax.swing.Timer(3000, e -> updateConnectionStatus());
|
statusTimer = new javax.swing.Timer(3000, e -> updateConnectionStatus());
|
||||||
statusTimer.start();
|
statusTimer.start();
|
||||||
setSize(880, 560);
|
setSize(UIScale.scale(new Dimension(880, 560)));
|
||||||
setLocationRelativeTo(null);
|
setLocationRelativeTo(null);
|
||||||
|
|
||||||
// Deferred so the window is on screen before we start connecting.
|
// 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());
|
JTextField labelField = new JTextField(selected.title());
|
||||||
JCheckBox joinChannel = new JCheckBox("Join \"" + channelPath + "\" on connect", !channelPath.isEmpty());
|
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(new JLabel("Bookmark label:"));
|
||||||
form.add(labelField);
|
form.add(labelField);
|
||||||
if (!channelPath.isEmpty()) form.add(joinChannel);
|
if (!channelPath.isEmpty()) form.add(joinChannel);
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.ScaledEmptyBorder;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.config.Settings;
|
import com.ts3client.config.Settings;
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
|
||||||
import javax.swing.Box;
|
import javax.swing.Box;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JCheckBoxMenuItem;
|
import javax.swing.JCheckBoxMenuItem;
|
||||||
@@ -74,7 +75,7 @@ final class MainToolbar extends JToolBar {
|
|||||||
|
|
||||||
setFloatable(false);
|
setFloatable(false);
|
||||||
setBackground(Theme.toolbarBg());
|
setBackground(Theme.toolbarBg());
|
||||||
setBorder(BorderFactory.createEmptyBorder(4, 6, 4, 6));
|
setBorder(new ScaledEmptyBorder(4, 6, 4, 6));
|
||||||
setComponentPopupMenu(buildContextMenu());
|
setComponentPopupMenu(buildContextMenu());
|
||||||
|
|
||||||
connectButton = new JButton(Icons.connect());
|
connectButton = new JButton(Icons.connect());
|
||||||
@@ -152,7 +153,7 @@ final class MainToolbar extends JToolBar {
|
|||||||
|
|
||||||
/** The Master Volume slider: multiplies both voice and notification volume. */
|
/** The Master Volume slider: multiplies both voice and notification volume. */
|
||||||
private JComponent buildMasterVolumeControl() {
|
private JComponent buildMasterVolumeControl() {
|
||||||
JPanel panel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 4, 0));
|
JPanel panel = new JPanel(new FlowLayout(FlowLayout.RIGHT, UIScale.scale(4), 0));
|
||||||
panel.setOpaque(false);
|
panel.setOpaque(false);
|
||||||
panel.setAlignmentY(Component.CENTER_ALIGNMENT);
|
panel.setAlignmentY(Component.CENTER_ALIGNMENT);
|
||||||
|
|
||||||
@@ -164,7 +165,7 @@ final class MainToolbar extends JToolBar {
|
|||||||
slider.setOpaque(false);
|
slider.setOpaque(false);
|
||||||
slider.setToolTipText("Master volume (voice + notifications) — scroll to adjust");
|
slider.setToolTipText("Master volume (voice + notifications) — scroll to adjust");
|
||||||
slider.setAlignmentY(Component.CENTER_ALIGNMENT);
|
slider.setAlignmentY(Component.CENTER_ALIGNMENT);
|
||||||
slider.setPreferredSize(new Dimension(135, slider.getPreferredSize().height));
|
slider.setPreferredSize(new Dimension(UIScale.scale(135), slider.getPreferredSize().height));
|
||||||
slider.addChangeListener(e -> {
|
slider.addChangeListener(e -> {
|
||||||
settings.masterVolume = slider.getValue() / 100.0;
|
settings.masterVolume = slider.getValue() / 100.0;
|
||||||
listener.onMasterVolumeChanged();
|
listener.onMasterVolumeChanged();
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.net.ClientEntry;
|
import com.ts3client.net.ClientEntry;
|
||||||
|
|
||||||
import javax.swing.DefaultCellEditor;
|
import javax.swing.DefaultCellEditor;
|
||||||
@@ -103,7 +104,7 @@ final class NicknameCellEditor extends DefaultTreeCellEditor {
|
|||||||
if (!(c instanceof JLabel)) return;
|
if (!(c instanceof JLabel)) return;
|
||||||
editingIcon = ((JLabel) c).getIcon();
|
editingIcon = ((JLabel) c).getIcon();
|
||||||
offset = editingIcon == null ? 0
|
offset = editingIcon == null ? 0
|
||||||
: editingIcon.getIconWidth() + renderer.getIconTextGap() - FIELD_NUDGE;
|
: editingIcon.getIconWidth() + renderer.getIconTextGap() - UIScale.scale(FIELD_NUDGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The edited nickname, trimmed; empty when nothing usable was typed. */
|
/** The edited nickname, trimmed; empty when nothing usable was typed. */
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.ScaledEmptyBorder;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.config.Settings;
|
import com.ts3client.config.Settings;
|
||||||
import com.ts3client.sound.NotificationSettings;
|
import com.ts3client.sound.NotificationSettings;
|
||||||
import com.ts3client.sound.SoundEvent;
|
import com.ts3client.sound.SoundEvent;
|
||||||
import com.ts3client.sound.SoundNotifier;
|
import com.ts3client.sound.SoundNotifier;
|
||||||
import com.ts3client.sound.SoundPack;
|
import com.ts3client.sound.SoundPack;
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JComboBox;
|
import javax.swing.JComboBox;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
@@ -79,7 +80,7 @@ final class NotificationsPanel extends JPanel {
|
|||||||
private final EventTableModel tableModel = new EventTableModel();
|
private final EventTableModel tableModel = new EventTableModel();
|
||||||
|
|
||||||
NotificationsPanel(Settings settings, SoundNotifier sounds) {
|
NotificationsPanel(Settings settings, SoundNotifier sounds) {
|
||||||
super(new BorderLayout(0, 8));
|
super(new BorderLayout(0, UIScale.scale(8)));
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
this.sounds = sounds;
|
this.sounds = sounds;
|
||||||
this.working = settings.notifications.copy();
|
this.working = settings.notifications.copy();
|
||||||
@@ -91,7 +92,7 @@ final class NotificationsPanel extends JPanel {
|
|||||||
buildRows();
|
buildRows();
|
||||||
this.table = buildTable();
|
this.table = buildTable();
|
||||||
|
|
||||||
setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));
|
setBorder(new ScaledEmptyBorder(12, 12, 12, 12));
|
||||||
add(buildHeader(), BorderLayout.NORTH);
|
add(buildHeader(), BorderLayout.NORTH);
|
||||||
add(new JScrollPane(table), BorderLayout.CENTER);
|
add(new JScrollPane(table), BorderLayout.CENTER);
|
||||||
add(buildFooter(), BorderLayout.SOUTH);
|
add(buildFooter(), BorderLayout.SOUTH);
|
||||||
@@ -122,7 +123,7 @@ final class NotificationsPanel extends JPanel {
|
|||||||
private JComponent buildHeader() {
|
private JComponent buildHeader() {
|
||||||
JPanel p = new JPanel(new GridBagLayout());
|
JPanel p = new JPanel(new GridBagLayout());
|
||||||
GridBagConstraints c = new GridBagConstraints();
|
GridBagConstraints c = new GridBagConstraints();
|
||||||
c.insets = new Insets(2, 2, 2, 2);
|
c.insets = UIScale.scale(new Insets(2, 2, 2, 2));
|
||||||
c.anchor = GridBagConstraints.WEST;
|
c.anchor = GridBagConstraints.WEST;
|
||||||
c.fill = GridBagConstraints.HORIZONTAL;
|
c.fill = GridBagConstraints.HORIZONTAL;
|
||||||
|
|
||||||
@@ -132,7 +133,7 @@ final class NotificationsPanel extends JPanel {
|
|||||||
test.setToolTipText("Play this pack's test sound");
|
test.setToolTipText("Play this pack's test sound");
|
||||||
test.addActionListener(e -> sounds.preview(SoundEvent.SPECIAL_SOUND_TEST));
|
test.addActionListener(e -> sounds.preview(SoundEvent.SPECIAL_SOUND_TEST));
|
||||||
|
|
||||||
JPanel packRow = new JPanel(new BorderLayout(6, 0));
|
JPanel packRow = new JPanel(new BorderLayout(UIScale.scale(6), 0));
|
||||||
packRow.add(packCombo, BorderLayout.CENTER);
|
packRow.add(packCombo, BorderLayout.CENTER);
|
||||||
packRow.add(test, BorderLayout.EAST);
|
packRow.add(test, BorderLayout.EAST);
|
||||||
|
|
||||||
@@ -146,7 +147,7 @@ final class NotificationsPanel extends JPanel {
|
|||||||
volume.setToolTipText("Volume of the notification sounds");
|
volume.setToolTipText("Volume of the notification sounds");
|
||||||
addRow(p, c, row++, new JLabel("Sound volume:"), volume);
|
addRow(p, c, row++, new JLabel("Sound volume:"), volume);
|
||||||
|
|
||||||
JPanel dirRow = new JPanel(new BorderLayout(6, 0));
|
JPanel dirRow = new JPanel(new BorderLayout(UIScale.scale(6), 0));
|
||||||
JButton browse = new JButton("Browse…");
|
JButton browse = new JButton("Browse…");
|
||||||
browse.addActionListener(e -> browseForPackFolder());
|
browse.addActionListener(e -> browseForPackFolder());
|
||||||
dirRow.add(packDirField, BorderLayout.CENTER);
|
dirRow.add(packDirField, BorderLayout.CENTER);
|
||||||
@@ -157,10 +158,10 @@ final class NotificationsPanel extends JPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private JComponent buildFooter() {
|
private JComponent buildFooter() {
|
||||||
JPanel p = new JPanel(new BorderLayout(6, 0));
|
JPanel p = new JPanel(new BorderLayout(UIScale.scale(6), 0));
|
||||||
JLabel hint = new JLabel("<html><b>Bold</b> actions are important: they are still played "
|
JLabel hint = new JLabel("<html><b>Bold</b> actions are important: they are still played "
|
||||||
+ "while your speakers are muted.</html>");
|
+ "while your speakers are muted.</html>");
|
||||||
hint.setBorder(BorderFactory.createEmptyBorder(4, 0, 0, 0));
|
hint.setBorder(new ScaledEmptyBorder(4, 0, 0, 0));
|
||||||
|
|
||||||
JPanel buttons = new JPanel();
|
JPanel buttons = new JPanel();
|
||||||
JButton important = new JButton("Toggle important");
|
JButton important = new JButton("Toggle important");
|
||||||
@@ -187,12 +188,12 @@ final class NotificationsPanel extends JPanel {
|
|||||||
t.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
t.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||||
t.setShowGrid(false);
|
t.setShowGrid(false);
|
||||||
t.setTableHeader(null);
|
t.setTableHeader(null);
|
||||||
t.setRowHeight(Math.max(t.getRowHeight(), t.getFontMetrics(t.getFont()).getHeight() + 6));
|
t.setRowHeight(Math.max(t.getRowHeight(), t.getFontMetrics(t.getFont()).getHeight() + UIScale.scale(6)));
|
||||||
t.getColumnModel().getColumn(0).setMaxWidth(TOGGLE_COLUMN_WIDTH);
|
t.getColumnModel().getColumn(0).setMaxWidth(UIScale.scale(TOGGLE_COLUMN_WIDTH));
|
||||||
t.getColumnModel().getColumn(0).setMinWidth(TOGGLE_COLUMN_WIDTH);
|
t.getColumnModel().getColumn(0).setMinWidth(UIScale.scale(TOGGLE_COLUMN_WIDTH));
|
||||||
t.getColumnModel().getColumn(0).setCellRenderer(new ToggleRenderer(t.getDefaultRenderer(Boolean.class)));
|
t.getColumnModel().getColumn(0).setCellRenderer(new ToggleRenderer(t.getDefaultRenderer(Boolean.class)));
|
||||||
t.getColumnModel().getColumn(1).setCellRenderer(new ActionRenderer());
|
t.getColumnModel().getColumn(1).setCellRenderer(new ActionRenderer());
|
||||||
t.setPreferredScrollableViewportSize(new Dimension(360, 260));
|
t.setPreferredScrollableViewportSize(UIScale.scale(new Dimension(360, 260)));
|
||||||
|
|
||||||
t.addMouseListener(new MouseAdapter() {
|
t.addMouseListener(new MouseAdapter() {
|
||||||
@Override
|
@Override
|
||||||
@@ -381,11 +382,11 @@ final class NotificationsPanel extends JPanel {
|
|||||||
if (row.isHeading()) {
|
if (row.isHeading()) {
|
||||||
setFont(base.deriveFont(Font.BOLD));
|
setFont(base.deriveFont(Font.BOLD));
|
||||||
setForeground(selected ? table.getSelectionForeground() : table.getForeground());
|
setForeground(selected ? table.getSelectionForeground() : table.getForeground());
|
||||||
setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
|
setBorder(new ScaledEmptyBorder(2, 2, 2, 2));
|
||||||
setToolTipText(null);
|
setToolTipText(null);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
setBorder(BorderFactory.createEmptyBorder(2, 18, 2, 2));
|
setBorder(new ScaledEmptyBorder(2, 18, 2, 2));
|
||||||
setFont(base.deriveFont(working.isImportant(row.event) ? Font.BOLD : Font.PLAIN));
|
setFont(base.deriveFont(working.isImportant(row.event) ? Font.BOLD : Font.PLAIN));
|
||||||
boolean silent = !sounds.hasSound(row.event);
|
boolean silent = !sounds.hasSound(row.event);
|
||||||
setForeground(selected ? table.getSelectionForeground()
|
setForeground(selected ? table.getSelectionForeground()
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
import com.formdev.flatlaf.util.ScaledEmptyBorder;
|
||||||
|
|
||||||
import javax.swing.BoxLayout;
|
import javax.swing.BoxLayout;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
@@ -54,7 +55,7 @@ final class ReasonDialog {
|
|||||||
static JLabel label(String text) {
|
static JLabel label(String text) {
|
||||||
JLabel label = new JLabel(text);
|
JLabel label = new JLabel(text);
|
||||||
label.setAlignmentX(Component.LEFT_ALIGNMENT);
|
label.setAlignmentX(Component.LEFT_ALIGNMENT);
|
||||||
label.setBorder(BorderFactory.createEmptyBorder(0, 0, 4, 0));
|
label.setBorder(new ScaledEmptyBorder(0, 0, 4, 0));
|
||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.ScaledEmptyBorder;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.net.ClientEntry;
|
import com.ts3client.net.ClientEntry;
|
||||||
import com.ts3client.net.Group;
|
import com.ts3client.net.Group;
|
||||||
import com.ts3client.net.TeamspeakConnection;
|
import com.ts3client.net.TeamspeakConnection;
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
|
||||||
import javax.swing.BoxLayout;
|
import javax.swing.BoxLayout;
|
||||||
import javax.swing.GrayFilter;
|
import javax.swing.GrayFilter;
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
@@ -48,10 +49,10 @@ final class ServerGroupsDialog extends JDialog {
|
|||||||
|
|
||||||
list.setLayout(new BoxLayout(list, BoxLayout.Y_AXIS));
|
list.setLayout(new BoxLayout(list, BoxLayout.Y_AXIS));
|
||||||
JScrollPane scroll = new JScrollPane(list);
|
JScrollPane scroll = new JScrollPane(list);
|
||||||
scroll.setPreferredSize(new Dimension(280, 360));
|
scroll.setPreferredSize(UIScale.scale(new Dimension(280, 360)));
|
||||||
scroll.getVerticalScrollBar().setUnitIncrement(16);
|
scroll.getVerticalScrollBar().setUnitIncrement(16);
|
||||||
|
|
||||||
header.setBorder(BorderFactory.createEmptyBorder(8, 10, 4, 10));
|
header.setBorder(new ScaledEmptyBorder(8, 10, 4, 10));
|
||||||
header.setHorizontalAlignment(SwingConstants.LEFT);
|
header.setHorizontalAlignment(SwingConstants.LEFT);
|
||||||
|
|
||||||
JButton close = new JButton("Close");
|
JButton close = new JButton("Close");
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.ScaledEmptyBorder;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
import javax.swing.BorderFactory;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
@@ -150,9 +153,9 @@ final class ServerTabPane extends JPanel {
|
|||||||
|
|
||||||
/** A tab's label: title, microphone marker and a close button. */
|
/** A tab's label: title, microphone marker and a close button. */
|
||||||
private JComponent buildTabComponent(ServerTab tab, boolean hasMic) {
|
private JComponent buildTabComponent(ServerTab tab, boolean hasMic) {
|
||||||
JPanel cell = new JPanel(new FlowLayout(FlowLayout.LEFT, 4, 0));
|
JPanel cell = new JPanel(new FlowLayout(FlowLayout.LEFT, UIScale.scale(4), 0));
|
||||||
cell.setOpaque(false);
|
cell.setOpaque(false);
|
||||||
cell.setBorder(BorderFactory.createEmptyBorder(1, 0, 1, 0));
|
cell.setBorder(new ScaledEmptyBorder(1, 0, 1, 0));
|
||||||
|
|
||||||
JLabel label = new JLabel(tab.title(), hasMic ? Icons.micActiveSmall() : null, JLabel.LEADING);
|
JLabel label = new JLabel(tab.title(), hasMic ? Icons.micActiveSmall() : null, JLabel.LEADING);
|
||||||
label.setFont(Theme.uiFont());
|
label.setFont(Theme.uiFont());
|
||||||
@@ -175,7 +178,7 @@ final class ServerTabPane extends JPanel {
|
|||||||
close.setContentAreaFilled(false);
|
close.setContentAreaFilled(false);
|
||||||
close.setMargin(new Insets(0, 0, 0, 0));
|
close.setMargin(new Insets(0, 0, 0, 0));
|
||||||
close.setForeground(Theme.chatSystem());
|
close.setForeground(Theme.chatSystem());
|
||||||
close.setPreferredSize(new Dimension(14, 14));
|
close.setPreferredSize(UIScale.scale(new Dimension(14, 14)));
|
||||||
close.setToolTipText("Close this connection");
|
close.setToolTipText("Close this connection");
|
||||||
close.addActionListener(e -> listener.closeTab(tab));
|
close.addActionListener(e -> listener.closeTab(tab));
|
||||||
cell.add(close);
|
cell.add(close);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.config.Settings;
|
import com.ts3client.config.Settings;
|
||||||
import com.ts3client.contacts.ContactStore;
|
import com.ts3client.contacts.ContactStore;
|
||||||
import com.ts3client.contacts.ContactType;
|
import com.ts3client.contacts.ContactType;
|
||||||
@@ -168,7 +169,7 @@ public final class ServerTreePanel extends JScrollPane {
|
|||||||
ui.setLeftChildIndent(4);
|
ui.setLeftChildIndent(4);
|
||||||
ui.setRightChildIndent(10);
|
ui.setRightChildIndent(10);
|
||||||
}
|
}
|
||||||
tree.setRowHeight(20);
|
tree.setRowHeight(UIScale.scale(20));
|
||||||
tree.setBackground(Theme.treeBg());
|
tree.setBackground(Theme.treeBg());
|
||||||
tree.setFont(Theme.uiFont());
|
tree.setFont(Theme.uiFont());
|
||||||
ServerTreeCellRenderer renderer = new ServerTreeCellRenderer(id -> id == selfClientId, contacts, settings);
|
ServerTreeCellRenderer renderer = new ServerTreeCellRenderer(id -> id == selfClientId, contacts, settings);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.audio.OpusParameters;
|
import com.ts3client.audio.OpusParameters;
|
||||||
import com.ts3client.audio.VoiceInput;
|
import com.ts3client.audio.VoiceInput;
|
||||||
import com.ts3client.audio.VoiceOutput;
|
import com.ts3client.audio.VoiceOutput;
|
||||||
@@ -92,8 +93,8 @@ public final class SettingsDialog extends JDialog {
|
|||||||
});
|
});
|
||||||
|
|
||||||
pack();
|
pack();
|
||||||
setSize(new Dimension(480, 540));
|
setSize(UIScale.scale(new Dimension(480, 540)));
|
||||||
setMinimumSize(new Dimension(420, 360));
|
setMinimumSize(UIScale.scale(new Dimension(420, 360)));
|
||||||
setLocationRelativeTo(owner);
|
setLocationRelativeTo(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
import javax.swing.JSpinner;
|
import javax.swing.JSpinner;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
|
|
||||||
@@ -19,7 +21,7 @@ final class Spinners {
|
|||||||
static JSpinner compact(JSpinner spinner) {
|
static JSpinner compact(JSpinner spinner) {
|
||||||
JSpinner.NumberEditor editor = new JSpinner.NumberEditor(spinner, "#");
|
JSpinner.NumberEditor editor = new JSpinner.NumberEditor(spinner, "#");
|
||||||
spinner.setEditor(editor);
|
spinner.setEditor(editor);
|
||||||
Dimension size = new Dimension(WIDTH, spinner.getPreferredSize().height);
|
Dimension size = new Dimension(UIScale.scale(WIDTH), spinner.getPreferredSize().height);
|
||||||
spinner.setPreferredSize(size);
|
spinner.setPreferredSize(size);
|
||||||
spinner.setMaximumSize(size);
|
spinner.setMaximumSize(size);
|
||||||
return spinner;
|
return spinner;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
import com.formdev.flatlaf.util.ScaledEmptyBorder;
|
||||||
|
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
@@ -14,7 +15,7 @@ final class StatusBar extends JPanel {
|
|||||||
StatusBar() {
|
StatusBar() {
|
||||||
super(new BorderLayout());
|
super(new BorderLayout());
|
||||||
setBackground(Theme.statusBg());
|
setBackground(Theme.statusBg());
|
||||||
setBorder(BorderFactory.createEmptyBorder(3, 8, 3, 8));
|
setBorder(new ScaledEmptyBorder(3, 8, 3, 8));
|
||||||
statusLabel.setFont(Theme.uiFont());
|
statusLabel.setFont(Theme.uiFont());
|
||||||
codecLabel.setFont(Theme.uiFont());
|
codecLabel.setFont(Theme.uiFont());
|
||||||
codecLabel.setForeground(Theme.chatSystem());
|
codecLabel.setForeground(Theme.chatSystem());
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
@@ -36,7 +37,7 @@ public final class Theme {
|
|||||||
|
|
||||||
public static Font uiFont() {
|
public static Font uiFont() {
|
||||||
Font f = UIManager.getFont("Label.font");
|
Font f = UIManager.getFont("Label.font");
|
||||||
return f != null ? f : new Font("SansSerif", Font.PLAIN, 12);
|
return f != null ? f : new Font("SansSerif", Font.PLAIN, UIScale.scale(12));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Font uiBold() {
|
public static Font uiBold() {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.ts3client.audio.InputLevel;
|
import com.ts3client.audio.InputLevel;
|
||||||
import com.ts3client.audio.OpusParameters;
|
import com.ts3client.audio.OpusParameters;
|
||||||
import com.ts3client.audio.VoiceInput;
|
import com.ts3client.audio.VoiceInput;
|
||||||
@@ -105,11 +106,11 @@ final class VoiceActivationPanel extends FormPanel {
|
|||||||
c.gridx = 0;
|
c.gridx = 0;
|
||||||
c.gridy = row;
|
c.gridy = row;
|
||||||
c.gridwidth = 2;
|
c.gridwidth = 2;
|
||||||
c.insets = new Insets(10, 4, 2, 4);
|
c.insets = UIScale.scale(new Insets(10, 4, 2, 4));
|
||||||
add(new JLabel("Input level:"), c);
|
add(new JLabel("Input level:"), c);
|
||||||
c.gridy = ++row;
|
c.gridy = ++row;
|
||||||
add(meter, c);
|
add(meter, c);
|
||||||
c.insets = new Insets(4, 4, 4, 4);
|
c.insets = UIScale.scale(new Insets(4, 4, 4, 4));
|
||||||
c.gridy = ++row;
|
c.gridy = ++row;
|
||||||
add(buildTestControls(), c);
|
add(buildTestControls(), c);
|
||||||
c.gridwidth = 1;
|
c.gridwidth = 1;
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
|
|
||||||
|
class DisplayScaleTest {
|
||||||
|
|
||||||
|
private static final List<String> KWINRC = List.of(
|
||||||
|
"[Plugins]",
|
||||||
|
"Scale=9",
|
||||||
|
"",
|
||||||
|
"[Xwayland]",
|
||||||
|
"Scale = 1.5",
|
||||||
|
"",
|
||||||
|
"[Xwayland][Extra]",
|
||||||
|
"Scale=2");
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void readsTheKeyFromItsOwnGroupOnly() {
|
||||||
|
assertEquals("1.5", DisplayScale.iniValue(KWINRC, "Xwayland", "Scale"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void missingKeyIsNull() {
|
||||||
|
assertNull(DisplayScale.iniValue(KWINRC, "Xwayland", "Other"));
|
||||||
|
assertNull(DisplayScale.iniValue(KWINRC, "Compositing", "Scale"));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user