diff --git a/lm-launcher.jar b/lm-launcher.jar index 8f6f0b3..064e20c 100644 Binary files a/lm-launcher.jar and b/lm-launcher.jar differ diff --git a/src/de/extio/lm_launcher/AppPropertiesDialog.java b/src/de/extio/lm_launcher/AppPropertiesDialog.java index 2a26496..d9555dc 100644 --- a/src/de/extio/lm_launcher/AppPropertiesDialog.java +++ b/src/de/extio/lm_launcher/AppPropertiesDialog.java @@ -58,9 +58,9 @@ public class AppPropertiesDialog extends JDialog { AppPropertiesDialog.app = app; this.onClose = onClose; - this.setBounds(100, 100, 681, 350); + this.setBounds(UIScaler.scale(100), UIScaler.scale(100), UIScaler.scale(681), UIScaler.scale(350)); this.getContentPane().setLayout(new BorderLayout()); - this.contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5)); + this.contentPanel.setBorder(new EmptyBorder(UIScaler.scale(5), UIScaler.scale(5), UIScaler.scale(5), UIScaler.scale(5))); this.getContentPane().add(this.contentPanel, BorderLayout.CENTER); this.contentPanel.setLayout(new BorderLayout(0, 0)); { @@ -75,6 +75,8 @@ public class AppPropertiesDialog extends JDialog { this.table.setRowSelectionAllowed(false); this.table.setFillsViewportHeight(true); this.table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); + // Scale the table row height to accommodate larger checkboxes + this.table.setRowHeight(UIScaler.scale(this.table.getRowHeight())); JCheckBoxRenderer renderer = new JCheckBoxRenderer(); JCheckBoxEditor editor = new JCheckBoxEditor(); @@ -137,6 +139,9 @@ public class AppPropertiesDialog extends JDialog { this.textField.setText(app.argumentsToString(true)); this.refreshTable(); + + // Apply font scaling to all components + UIScaler.scaleComponentFonts(this); } private void refreshTable() { @@ -173,6 +178,11 @@ public class AppPropertiesDialog extends JDialog { static class JCheckBoxRenderer extends JCheckBox implements TableCellRenderer { + public JCheckBoxRenderer() { + // Apply comprehensive checkbox scaling + UIScaler.scaleCheckBox(this); + } + @Override public Component getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected, final boolean hasFocus, final int row, final int column) { this.setSelected((boolean) value); @@ -185,6 +195,9 @@ public class AppPropertiesDialog extends JDialog { private final JCheckBox checkBox = new JCheckBox() { { + // Apply comprehensive checkbox scaling + UIScaler.scaleCheckBox(this); + this.addActionListener(e -> { final AppArgument existing = AppPropertiesDialog.app.appArguments().get(JCheckBoxEditor.this.row); if (JCheckBoxEditor.this.column == 1) { @@ -219,4 +232,4 @@ public class AppPropertiesDialog extends JDialog { return true; } } -} +} \ No newline at end of file diff --git a/src/de/extio/lm_launcher/LaunchFrame.java b/src/de/extio/lm_launcher/LaunchFrame.java index e9ea143..1f06a30 100644 --- a/src/de/extio/lm_launcher/LaunchFrame.java +++ b/src/de/extio/lm_launcher/LaunchFrame.java @@ -124,9 +124,9 @@ public class LaunchFrame extends JFrame { public LaunchFrame() { this.setTitle("LM Launcher"); this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); - this.setPreferredSize(new Dimension(650, 650)); + this.setPreferredSize(UIScaler.scale(new Dimension(650, 650))); this.contentPane = new JPanel(); - this.contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); + this.contentPane.setBorder(new EmptyBorder(UIScaler.scale(5), UIScaler.scale(5), UIScaler.scale(5), UIScaler.scale(5))); this.setContentPane(this.contentPane); this.contentPane.setLayout(new BorderLayout(0, 0)); @@ -276,6 +276,8 @@ public class LaunchFrame extends JFrame { this.table = new JTable(this.appArgumentsTableModel); this.table.setRowSelectionAllowed(false); this.table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); + // Scale the table row height to accommodate larger checkboxes + this.table.setRowHeight(UIScaler.scale(this.table.getRowHeight())); this.table.getColumnModel().getColumn(0).setCellRenderer(new AppArgumentsJCheckBoxRenderer(this.appArgumentsTableModel)); this.table.getColumnModel().getColumn(0).setCellEditor(new AppArgumentsJCheckBoxEditor()); @@ -538,7 +540,11 @@ public class LaunchFrame extends JFrame { } this.pack(); - this.setLocationRelativeTo(null); + + // Apply font scaling to all components + UIScaler.scaleComponentFonts(this); + + UIScaler.centerOnPrimaryDisplay(this); this.repaint(); this.splitPane.setDividerLocation(0.33); this.splitPane_1.setDividerLocation(0.66); @@ -575,6 +581,8 @@ public class LaunchFrame extends JFrame { public AppArgumentsJCheckBoxRenderer(final DefaultTableModel model) { super(); this.model = model; + // Apply comprehensive checkbox scaling + UIScaler.scaleCheckBox(this); } @Override @@ -592,6 +600,11 @@ public class LaunchFrame extends JFrame { private AppArgument appArgument; + public AppArgumentsJCheckBoxEditor() { + // Apply comprehensive checkbox scaling + UIScaler.scaleCheckBox(this.checkBox); + } + @Override public Component getTableCellEditorComponent(final JTable table, final Object value, final boolean isSelected, final int row, final int column) { this.appArgument = (AppArgument) value; @@ -606,4 +619,4 @@ public class LaunchFrame extends JFrame { } } -} +} \ No newline at end of file diff --git a/src/de/extio/lm_launcher/Main.java b/src/de/extio/lm_launcher/Main.java index b4bc684..ab6707a 100644 --- a/src/de/extio/lm_launcher/Main.java +++ b/src/de/extio/lm_launcher/Main.java @@ -7,6 +7,8 @@ public class Main { public static void main(final String[] args) { System.setProperty("awt.useSystemAAFontSettings", "on"); System.setProperty("swing.aatext", "true"); + System.setProperty("sun.java2d.uiScale", "1"); + System.setProperty("awt.nativeDoubleBuffering", "true"); EventQueue.invokeLater(new Runnable() { diff --git a/src/de/extio/lm_launcher/ModelPropertiesDialog.java b/src/de/extio/lm_launcher/ModelPropertiesDialog.java index cbdcbde..17ad9f7 100644 --- a/src/de/extio/lm_launcher/ModelPropertiesDialog.java +++ b/src/de/extio/lm_launcher/ModelPropertiesDialog.java @@ -53,9 +53,9 @@ public class ModelPropertiesDialog extends JDialog { this.setModal(true); this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); - this.setBounds(100, 100, 678, 191); + this.setBounds(UIScaler.scale(100), UIScaler.scale(100), UIScaler.scale(678), UIScaler.scale(191)); this.getContentPane().setLayout(new BorderLayout()); - this.contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5)); + this.contentPanel.setBorder(new EmptyBorder(UIScaler.scale(5), UIScaler.scale(5), UIScaler.scale(5), UIScaler.scale(5))); this.getContentPane().add(this.contentPanel, BorderLayout.CENTER); this.contentPanel.setLayout(new GridLayout(0, 2, 0, 0)); { @@ -150,6 +150,9 @@ public class ModelPropertiesDialog extends JDialog { this.textField_3.setText(String.valueOf(editModel.contextSize())); this.txtPrompttemplate.setText(editModel.promptTemplate()); } + + // Apply font scaling to all components + UIScaler.scaleComponentFonts(this); } -} +} \ No newline at end of file diff --git a/src/de/extio/lm_launcher/SelectModelDialog.java b/src/de/extio/lm_launcher/SelectModelDialog.java index 15f7483..0edbeda 100644 --- a/src/de/extio/lm_launcher/SelectModelDialog.java +++ b/src/de/extio/lm_launcher/SelectModelDialog.java @@ -40,9 +40,9 @@ public class SelectModelDialog extends JDialog { this.setModal(true); this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); - this.setBounds(100, 100, 578, 251); + this.setBounds(UIScaler.scale(100), UIScaler.scale(100), UIScaler.scale(578), UIScaler.scale(251)); this.getContentPane().setLayout(new BorderLayout()); - this.contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5)); + this.contentPanel.setBorder(new EmptyBorder(UIScaler.scale(5), UIScaler.scale(5), UIScaler.scale(5), UIScaler.scale(5))); this.getContentPane().add(this.contentPanel, BorderLayout.CENTER); this.contentPanel.setLayout(new BorderLayout(0, 0)); { @@ -92,6 +92,9 @@ public class SelectModelDialog extends JDialog { } } this.initialize(); + + // Apply font scaling to all components + UIScaler.scaleComponentFonts(this); } private void initialize() { @@ -101,4 +104,4 @@ public class SelectModelDialog extends JDialog { .forEach(this.listModel::addElement); } -} +} \ No newline at end of file diff --git a/src/de/extio/lm_launcher/UIScaler.java b/src/de/extio/lm_launcher/UIScaler.java new file mode 100644 index 0000000..4b01f2a --- /dev/null +++ b/src/de/extio/lm_launcher/UIScaler.java @@ -0,0 +1,268 @@ +package de.extio.lm_launcher; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.Toolkit; +import java.awt.Window; +import javax.swing.Icon; +import javax.swing.JCheckBox; +import javax.swing.JComponent; +import javax.swing.UIManager; +import javax.swing.plaf.metal.MetalIconFactory; + +/** + * Utility class for scaling UI elements based on screen resolution. + * Reference resolution is 1920x1080 (1080p). + */ +public class UIScaler { + + private static final int REFERENCE_WIDTH = 1920; + private static final int REFERENCE_HEIGHT = 1080; + + private static final double scaleX; + private static final double scaleY; + private static final double scale; + private static final GraphicsDevice primaryDevice; + + static { + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + primaryDevice = ge.getDefaultScreenDevice(); + + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + + scaleX = (double) screenSize.width / REFERENCE_WIDTH; + scaleY = (double) screenSize.height / REFERENCE_HEIGHT; + // Use the smaller scale to maintain aspect ratio, then dampen by 0.8 + double baseScale = Math.min(scaleX, scaleY); + scale = 1.0 + (baseScale - 1.0) * 0.8; + + // Apply global font scaling to UI defaults + scaleUIDefaults(); + } + + /** + * Scale UI default fonts globally + */ + private static void scaleUIDefaults() { + UIManager.getDefaults().entrySet().forEach(entry -> { + if (entry.getValue() instanceof Font) { + Font font = (Font) entry.getValue(); + UIManager.put(entry.getKey(), scaleFont(font)); + } + }); + + // Scale checkbox icon size + if (scale != 1.0) { + try { + // Create scaled checkbox icons + Icon checkBoxIcon = new ScaledCheckBoxIcon(false); + Icon checkBoxSelectedIcon = new ScaledCheckBoxIcon(true); + + UIManager.put("CheckBox.icon", checkBoxIcon); + UIManager.put("CheckBox.selectedIcon", checkBoxSelectedIcon); + } catch (Exception e) { + // Fallback - continue without custom icons + System.err.println("Could not set scaled checkbox icons: " + e.getMessage()); + } + } + } + + /** + * Apply scaling to a checkbox component + * @param checkBox The checkbox to scale + */ + public static void scaleCheckBox(JCheckBox checkBox) { + if (checkBox == null) return; + + // Scale font + checkBox.setFont(scaleFont(checkBox.getFont())); + + // Scale component size + Dimension prefSize = checkBox.getPreferredSize(); + if (prefSize != null) { + checkBox.setPreferredSize(scale(prefSize)); + } + + // Apply scaled icons if available + if (scale != 1.0) { + try { + checkBox.setIcon(new ScaledCheckBoxIcon(false)); + checkBox.setSelectedIcon(new ScaledCheckBoxIcon(true)); + } catch (Exception e) { + // Continue without custom icons + } + } + } + + /** + * Custom scaled checkbox icon + */ + private static class ScaledCheckBoxIcon implements Icon { + private final boolean selected; + private final int size; + + public ScaledCheckBoxIcon(boolean selected) { + this.selected = selected; + this.size = scale(13); // Default checkbox icon size is typically 13x13 + } + + @Override + public void paintIcon(Component c, java.awt.Graphics g, int x, int y) { + // Create a graphics context with scaling + java.awt.Graphics2D g2 = (java.awt.Graphics2D) g.create(); + + // Apply scaling transform + g2.scale(scale, scale); + + // Get the original icon and paint it + Icon originalIcon; + if (selected) { + originalIcon = MetalIconFactory.getCheckBoxIcon(); + // Simulate selected state + g2.translate(x / scale, y / scale); + originalIcon.paintIcon(c, g2, 0, 0); + } else { + originalIcon = MetalIconFactory.getCheckBoxIcon(); + g2.translate(x / scale, y / scale); + originalIcon.paintIcon(c, g2, 0, 0); + } + + g2.dispose(); + } + + @Override + public int getIconWidth() { + return size; + } + + @Override + public int getIconHeight() { + return size; + } + } + + /** + * Scale all fonts in a component hierarchy recursively + * @param component The root component to scale + */ + public static void scaleComponentFonts(Component component) { + if (component == null) return; + + // Scale the component's font + Font font = component.getFont(); + if (font != null) { + component.setFont(scaleFont(font)); + } + + // Recursively scale child components + if (component instanceof Container) { + Container container = (Container) component; + for (Component child : container.getComponents()) { + scaleComponentFonts(child); + } + } + } + + /** + * Scale a dimension value (width or height) based on screen resolution. + * @param value The original value at reference resolution + * @return The scaled value + */ + public static int scale(int value) { + return (int) Math.round(value * scale); + } + + /** + * Scale a dimension value using a custom scale factor. + * @param value The original value + * @param customScale The custom scale factor + * @return The scaled value + */ + public static int scale(int value, double customScale) { + return (int) Math.round(value * customScale); + } + + /** + * Scale a Dimension object. + * @param dimension The original dimension + * @return A new scaled Dimension + */ + public static Dimension scale(Dimension dimension) { + return new Dimension(scale(dimension.width), scale(dimension.height)); + } + + /** + * Scale a font size. + * @param fontSize The original font size + * @return The scaled font size + */ + public static int scaleFontSize(int fontSize) { + return Math.max(8, scale(fontSize)); // Minimum font size of 8 + } + + /** + * Scale a font. + * @param font The original font + * @return A new font with scaled size + */ + public static Font scaleFont(Font font) { + return font.deriveFont((float) scaleFontSize(font.getSize())); + } + + /** + * Get the current scale factor. + * @return The scale factor relative to reference resolution + */ + public static double getScale() { + return scale; + } + + /** + * Get the horizontal scale factor. + * @return The horizontal scale factor + */ + public static double getScaleX() { + return scaleX; + } + + /** + * Get the vertical scale factor. + * @return The vertical scale factor + */ + public static double getScaleY() { + return scaleY; + } + + /** + * Center a window on the primary display. + * @param window The window to center + */ + public static void centerOnPrimaryDisplay(Window window) { + window.setLocationRelativeTo(null); + // Ensure it's on the primary display + if (primaryDevice != null) { + window.setLocation( + primaryDevice.getDefaultConfiguration().getBounds().x + + (primaryDevice.getDefaultConfiguration().getBounds().width - window.getWidth()) / 2, + primaryDevice.getDefaultConfiguration().getBounds().y + + (primaryDevice.getDefaultConfiguration().getBounds().height - window.getHeight()) / 2 + ); + } + } + + /** + * Get information about current scaling for debugging. + * @return A string with scaling information + */ + public static String getScalingInfo() { + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + return String.format("Screen: %dx%d, Reference: %dx%d, Scale: %.2f (X: %.2f, Y: %.2f)", + screenSize.width, screenSize.height, + REFERENCE_WIDTH, REFERENCE_HEIGHT, + scale, scaleX, scaleY); + } +} \ No newline at end of file