diff --git a/bin/jmeter.properties b/bin/jmeter.properties index dd4dd7eaacd..707f9fcf15a 100644 --- a/bin/jmeter.properties +++ b/bin/jmeter.properties @@ -1182,11 +1182,15 @@ cookies=cookies #jsyntaxtextarea.wrapstyleword=true #jsyntaxtextarea.linewrap=true #jsyntaxtextarea.codefolding=true +#jsyntaxtextarea.highlight=true +#jsyntaxtextarea.tabsize=4 # Set 0 to disable undo feature in JSyntaxTextArea #jsyntaxtextarea.maxundos=50 # Change the font on the (JSyntax) Text Areas. (Useful for HiDPI screens) -#jsyntaxtextarea.font.family=Hack -#jsyntaxtextarea.font.size=14 +#jsyntaxtextarea.font.family= +#jsyntaxtextarea.font.size=-1 +#jsyntaxtextarea.theme.default=themes/default.xml +#jsyntaxtextarea.theme.dark=themes/dark.xml # Set this to false to disable the use of JSyntaxTextArea for the Console Logger panel #loggerpanel.usejsyntaxtext=true @@ -1388,3 +1392,6 @@ jmeter.reportgenerator.apdex_tolerated_threshold=1500 # Path to XSL file used to generate Schematic View of Test Plan # When empty, JMeter will use the embedded one in src/core/org/apache/jmeter/gui/action/schematic.xsl #docgeneration.schematic_xsl= + +#gui Zoom In/Out ratio +zoom_scale=1.1f diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index eedbce6676b..1c685949ea4 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -95,6 +95,7 @@ + diff --git a/src/bom-thirdparty/build.gradle.kts b/src/bom-thirdparty/build.gradle.kts index 7a7307e9f21..09bca1047db 100644 --- a/src/bom-thirdparty/build.gradle.kts +++ b/src/bom-thirdparty/build.gradle.kts @@ -43,7 +43,7 @@ dependencies { api("bsf:bsf:2.4.0") api("cglib:cglib-nodep:3.3.0") - api("com.fifesoft:rsyntaxtextarea:3.6.2") + api("com.fifesoft:rsyntaxtextarea:4.0.1") api("com.github.ben-manes.caffeine:caffeine:3.2.3") api("com.github.weisj:darklaf-core:3.1.1") api("com.github.weisj:darklaf-extensions-rsyntaxarea:0.4.1") @@ -146,5 +146,17 @@ dependencies { api("xalan:serializer:2.7.3") api("xalan:xalan:2.7.3") api("xml-apis:xml-apis:1.4.01") + //FlatLaf: https://www.formdev.com/flatlaf/native-libraries/#gradle + val flatlafVersion = "3.7.2" + api("com.formdev:flatlaf:${flatlafVersion}" ) + api("com.formdev:flatlaf:${flatlafVersion}:linux-x86_64@so") + api("com.formdev:flatlaf:${flatlafVersion}:macos-x86_64@dylib") + api("com.formdev:flatlaf:${flatlafVersion}:windows-x86_64@dll") + api("com.formdev:flatlaf-intellij-themes:${flatlafVersion}") + api("com.formdev:flatlaf-extras:${flatlafVersion}") + api("com.formdev:flatlaf-fonts-inter:4.1") + api("com.formdev:flatlaf-fonts-jetbrains-mono:2.304") + api("com.formdev:flatlaf-fonts-roboto:2.137") + api("com.formdev:flatlaf-fonts-roboto-mono:3.000") } } diff --git a/src/core/build.gradle.kts b/src/core/build.gradle.kts index c7785a39699..7cd222c6cb6 100644 --- a/src/core/build.gradle.kts +++ b/src/core/build.gradle.kts @@ -124,6 +124,9 @@ dependencies { testFixturesApi(testFixtures(projects.src.jorphan)) testFixturesImplementation(projects.src.testkit) testFixturesImplementation("org.junit.jupiter:junit-jupiter") + + implementation("com.formdev:flatlaf") + implementation("com.formdev:flatlaf-intellij-themes") } val generatedVersionDir = layout.buildDirectory.dir("generated/sources/version") diff --git a/src/core/src/main/java/org/apache/jmeter/SplashScreen.java b/src/core/src/main/java/org/apache/jmeter/SplashScreen.java index d7cfa8005e8..9fd4d85267c 100644 --- a/src/core/src/main/java/org/apache/jmeter/SplashScreen.java +++ b/src/core/src/main/java/org/apache/jmeter/SplashScreen.java @@ -30,6 +30,7 @@ import javax.swing.SwingUtilities; import javax.swing.border.EmptyBorder; +import org.apache.jmeter.gui.action.LookAndFeelCommand; import org.apache.jmeter.util.JMeterUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -74,7 +75,7 @@ public static JComponent loadLogo() { log.warn("Unable to find logo {}", svgResourcePath, e); } - if (svgUri != null) { + if (svgUri != null && LookAndFeelCommand.isDarklafTheme()) { Icon icon = new ThemedSVGIcon(svgUri, 521, 177); logo.setIcon(icon); } else { diff --git a/src/core/src/main/java/org/apache/jmeter/gui/LoggerPanel.java b/src/core/src/main/java/org/apache/jmeter/gui/LoggerPanel.java index fc88e57b426..a595f0e7daf 100644 --- a/src/core/src/main/java/org/apache/jmeter/gui/LoggerPanel.java +++ b/src/core/src/main/java/org/apache/jmeter/gui/LoggerPanel.java @@ -77,11 +77,8 @@ private JTextArea init() { // WARNING: called from ctor so must not be overridde JSyntaxTextArea jSyntaxTextArea = JSyntaxTextArea.getInstance(15, 80, true); jSyntaxTextArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_NONE); jSyntaxTextArea.setCodeFoldingEnabled(false); - jSyntaxTextArea.setAntiAliasingEnabled(false); - jSyntaxTextArea.setEditable(false); - jSyntaxTextArea.setLineWrap(false); + jSyntaxTextArea.setAntiAliasingEnabled(true); jSyntaxTextArea.setLanguage("text"); - jSyntaxTextArea.setMargin(new Insets(2, 2, 2, 2)); // space between borders and text areaScrollPane = JTextScrollPane.getInstance(jSyntaxTextArea); jTextArea = jSyntaxTextArea; } else { @@ -89,6 +86,9 @@ private JTextArea init() { // WARNING: called from ctor so must not be overridde jTextArea = new JTextArea(15, 80); areaScrollPane = new JScrollPane(jTextArea); } + jTextArea.setEditable(false); + jTextArea.setLineWrap(false); + jTextArea.setMargin(new Insets(2, 2, 2, 2)); // space between borders and text areaScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); areaScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); diff --git a/src/core/src/main/java/org/apache/jmeter/gui/MainFrame.java b/src/core/src/main/java/org/apache/jmeter/gui/MainFrame.java index bab760e6944..b08a18204f6 100644 --- a/src/core/src/main/java/org/apache/jmeter/gui/MainFrame.java +++ b/src/core/src/main/java/org/apache/jmeter/gui/MainFrame.java @@ -247,7 +247,7 @@ public MainFrame(TreeModel treeModel, JMeterTreeListener treeListener) { // Shift down means "horizontal scrolling" on macOS, and we need only vertical one if ((e.getModifiersEx() & (ctrlAltMask | InputEvent.SHIFT_DOWN_MASK)) == ctrlAltMask) { e.consume(); - final float scale = 1.1f; + final float scale = JMeterUtils.getPropDefault("zoom_scale", 1.1f); int rotation = e.getWheelRotation(); if (rotation > 0) { // DOWN JMeterUtils.applyScaleOnFonts(1.0f / scale); @@ -642,7 +642,7 @@ private static JScrollPane createMainPanel() { private static LoggerPanel createLoggerPanel() { LoggerPanel loggerPanel = new LoggerPanel(); loggerPanel.setMinimumSize(new Dimension(0, 100)); - loggerPanel.setPreferredSize(new Dimension(0, 150)); + loggerPanel.setPreferredSize(new Dimension(0, 100)); GuiPackage guiInstance = GuiPackage.getInstance(); guiInstance.setLoggerPanel(loggerPanel); guiInstance.getMenuItemLoggerPanel().getModel().setSelected(DISPLAY_LOGGER_PANEL); diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/LookAndFeelCommand.java b/src/core/src/main/java/org/apache/jmeter/gui/action/LookAndFeelCommand.java index be7f62c379c..bbf59e75b6a 100644 --- a/src/core/src/main/java/org/apache/jmeter/gui/action/LookAndFeelCommand.java +++ b/src/core/src/main/java/org/apache/jmeter/gui/action/LookAndFeelCommand.java @@ -36,9 +36,12 @@ import org.apache.jmeter.gui.util.JMeterMenuBar; import org.apache.jmeter.util.JMeterUtils; import org.apache.jorphan.gui.JFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.formdev.flatlaf.FlatLaf; +import com.formdev.flatlaf.intellijthemes.FlatAllIJThemes; import com.github.weisj.darklaf.LafManager; -import com.github.weisj.darklaf.theme.DarculaTheme; import com.github.weisj.darklaf.theme.Theme; import com.google.auto.service.AutoService; @@ -47,6 +50,7 @@ */ @AutoService(Command.class) public class LookAndFeelCommand extends AbstractAction { + private static final Logger log = LoggerFactory.getLogger(LookAndFeelCommand.class); private static final String JMETER_LAF = "jmeter.laf"; // $NON-NLS-1$ private static final Map items = new LinkedHashMap<>(); @@ -106,11 +110,29 @@ private static MenuItem ofDarklafTheme(Theme theme) { if (System.getProperty("darklaf.treeRowPopup") == null) { System.setProperty("darklaf.treeRowPopup", "false"); } - UIManager.installLookAndFeel(JMeterMenuBar.DARCULA_LAF, JMeterMenuBar.DARCULA_LAF_CLASS); + + //Add FlatLAF Themes + for (String flatLaf : new String[]{"FlatLightLaf", "FlatDarkLaf", "FlatIntelliJLaf", "FlatDarculaLaf"}) { + try { + UIManager.installLookAndFeel(flatLaf, "com.formdev.flatlaf." + flatLaf); + } catch( Exception ex ) { + log.warn("Failed to load FlatLAF theme: {}", flatLaf, ex); + } + } + for (String flatLaf : new String[]{"FlatMacDarkLaf", "FlatMacLightLaf"}) { + try { + UIManager.installLookAndFeel(flatLaf, "com.formdev.flatlaf.themes" + flatLaf); + } catch( Exception ex ) { + log.warn("Failed to load FlatLAF theme: {}", flatLaf, ex); + } + } + for (UIManager.LookAndFeelInfo lafInfo : FlatAllIJThemes.INFOS) { + UIManager.installLookAndFeel(lafInfo.getName(), lafInfo.getClassName()); + } List items = new ArrayList<>(); for (UIManager.LookAndFeelInfo laf : JMeterMenuBar.getAllLAFs()) { - if (!laf.getClassName().equals(JMeterMenuBar.DARCULA_LAF_CLASS)) { + if (!laf.getClassName().equals(JMeterMenuBar.DARKLAF_LAF_CLASS)) { items.add(MenuItem.of(laf.getName(), laf.getClassName())); } else { for (Theme theme : LafManager.getRegisteredThemes()) { @@ -143,15 +165,10 @@ public static Collection getMenuItems() { */ @Deprecated public static String getJMeterLaf(){ - String laf = PREFS.get(USER_PREFS_KEY, null); - if (laf != null) { - return checkLafName(laf); - } - String osName = System.getProperty("os.name") // $NON-NLS-1$ .toLowerCase(Locale.ENGLISH); // Spaces are not allowed in property names read from files - laf = JMeterUtils.getProperty(JMETER_LAF+"."+osName.replace(' ', '_')); + String laf = JMeterUtils.getProperty(JMETER_LAF+"."+osName.replace(' ', '_')); if (laf != null) { return checkLafName(laf); } @@ -160,10 +177,7 @@ public static String getJMeterLaf(){ if (laf != null) { return checkLafName(laf); } - laf = JMeterUtils.getPropDefault(JMETER_LAF, JMeterMenuBar.DARCULA_LAF_CLASS); - if (laf != null) { - return checkLafName(laf); - } + return UIManager.getCrossPlatformLookAndFeelClassName(); } @@ -174,16 +188,16 @@ public static String getJMeterLaf(){ public static String getPreferredLafCommand() { String laf = PREFS.get(USER_PREFS_KEY, null); if (laf != null) { - return laf; + MenuItem item = items.get(laf); + if (item == null) { + log.warn("LookAndFeel command '{}' not found in available items, falling back to default LAFs", laf); + } else { + return item.command; + } } String jMeterLaf = getJMeterLaf(); - if (jMeterLaf.equals(JMeterMenuBar.DARCULA_LAF_CLASS)) { - // Convert old Darcula to new Darklaf-Darcula LaF - return MenuItem.ofDarklafTheme(new DarculaTheme()).command; - } - - return MenuItem.of("default", jMeterLaf).command; // $NON-NLS-1$ + return MenuItem.of("default", jMeterLaf).command; // $NON-NLS-1$ } // Check if LAF is a built-in one @@ -205,8 +219,12 @@ public static boolean isDarklafTheme() { return LafManager.isInstalled(); } + public static boolean isFlatlafTheme() { + return UIManager.getLookAndFeel() instanceof FlatLaf; + } + public static boolean isDark() { - return isDarklafTheme() && Theme.isDark(LafManager.getTheme()); + return (isDarklafTheme() && Theme.isDark(LafManager.getTheme())) || (isFlatlafTheme() && ((FlatLaf)UIManager.getLookAndFeel()).isDark()); } public static void activateLookAndFeel(String command) { diff --git a/src/core/src/main/java/org/apache/jmeter/gui/util/JMeterMenuBar.java b/src/core/src/main/java/org/apache/jmeter/gui/util/JMeterMenuBar.java index 48ede565b83..e287d7d4c75 100644 --- a/src/core/src/main/java/org/apache/jmeter/gui/util/JMeterMenuBar.java +++ b/src/core/src/main/java/org/apache/jmeter/gui/util/JMeterMenuBar.java @@ -55,6 +55,7 @@ import org.apache.jmeter.util.LocaleChangeEvent; import org.apache.jmeter.util.LocaleChangeListener; import org.apache.jmeter.util.SSLManager; +import org.apache.jorphan.gui.GuiUtils; import org.apache.jorphan.reflect.LogAndIgnoreServiceLoadExceptionHandler; import org.apache.jorphan.util.JOrphanUtils; import org.apache.logging.log4j.Level; @@ -103,10 +104,10 @@ public class JMeterMenuBar extends JMenuBar implements LocaleChangeListener { public static final String SYSTEM_LAF = "System"; // $NON-NLS-1$ public static final String CROSS_PLATFORM_LAF = "CrossPlatform"; // $NON-NLS-1$ - public static final String DARCULA_LAF = "Darcula"; // $NON-NLS-1$ public static final String DARKLAF_LAF = "Darklaf"; // $NON-NLS-1$ - public static final String DARCULA_LAF_CLASS = "com.bulenkov.darcula.DarculaLaf"; // $NON-NLS-1$ + public static final String FLAT_LAF = "FlatLaf"; // $NON-NLS-1$ public static final String DARKLAF_LAF_CLASS = "com.github.weisj.darklaf.DarkLaf"; // $NON-NLS-1$ + public static final String FLATLAF_LAF_CLASS = "com.formdev.flatlaf.FlatLightLaf"; // $NON-NLS-1$ public JMeterMenuBar() { // List for recent files menu items @@ -319,6 +320,7 @@ private void makeOptionsMenu() { private static JMenu createLaFMenu() { JMenu lafMenu = makeMenuRes("appearance", 'L'); + JMenu flatLafSubMenu = new JMenu("FlatLaf Themes"); ButtonGroup lafGroup = new ButtonGroup(); String currentLafCommand = LookAndFeelCommand.getPreferredLafCommand(); for (LookAndFeelCommand.MenuItem item : LookAndFeelCommand.getMenuItems()) { @@ -328,9 +330,16 @@ private static JMenu createLaFMenu() { if (item.getCommand().equals(currentLafCommand)) { menuItem.setSelected(true); } + if (item.getCommand().startsWith("laf:com.formdev.flatlaf")) { + flatLafSubMenu.add(menuItem); + } else { + lafMenu.add(menuItem); + } lafGroup.add(menuItem); - lafMenu.add(menuItem); } + GuiUtils.makeScrollableMenu(flatLafSubMenu); + lafMenu.addSeparator(); + lafMenu.add(flatLafSubMenu); return lafMenu; } diff --git a/src/core/src/main/java/org/apache/jmeter/gui/util/JSyntaxTextArea.java b/src/core/src/main/java/org/apache/jmeter/gui/util/JSyntaxTextArea.java index 6625fa75777..e2fe19e46dc 100644 --- a/src/core/src/main/java/org/apache/jmeter/gui/util/JSyntaxTextArea.java +++ b/src/core/src/main/java/org/apache/jmeter/gui/util/JSyntaxTextArea.java @@ -35,7 +35,9 @@ import org.apache.jorphan.gui.JMeterUIDefaults; import org.apache.jorphan.gui.ui.TextComponentUI; import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; +import org.fife.ui.rsyntaxtextarea.Style; import org.fife.ui.rsyntaxtextarea.SyntaxConstants; +import org.fife.ui.rsyntaxtextarea.SyntaxScheme; import org.fife.ui.rsyntaxtextarea.Theme; import org.fife.ui.rtextarea.RUndoManager; import org.slf4j.Logger; @@ -53,7 +55,8 @@ public class JSyntaxTextArea extends RSyntaxTextArea { private static final long serialVersionUID = 211L; private static final Logger log = LoggerFactory.getLogger(JSyntaxTextArea.class); - private static final Theme DEFAULT_THEME = loadTheme(Theme.class, "themes/default.xml"); + private static final Theme DEFAULT_THEME = loadTheme(Theme.class, JMeterUtils.getPropDefault("jsyntaxtextarea.theme.default", "themes/default.xml")); + private static final Theme DEFAULT_DARK_THEME = loadTheme(Theme.class, JMeterUtils.getPropDefault("jsyntaxtextarea.theme.dark", "themes/dark.xml")); private final Properties languageProperties = JMeterUtils.loadProperties("org/apache/jmeter/gui/util/textarea.properties"); //$NON-NLS-1$ @@ -64,6 +67,8 @@ public class JSyntaxTextArea extends RSyntaxTextArea { private static final int MAX_UNDOS = JMeterUtils.getPropDefault("jsyntaxtextarea.maxundos", 50); private static final String USER_FONT_FAMILY = JMeterUtils.getPropDefault("jsyntaxtextarea.font.family", null); private static final int USER_FONT_SIZE = JMeterUtils.getPropDefault("jsyntaxtextarea.font.size", -1); + private static final boolean HIGHLIGHT_OCCURRENCES = JMeterUtils.getPropDefault("jsyntaxtextarea.highlight", true); + private static final int TAB_SIZE = JMeterUtils.getPropDefault("jsyntaxtextarea.tabsize", 4); private static final HierarchyListener GUTTER_THEME_PATCHER = e -> { if ((e.getChangeFlags() & HierarchyEvent.PARENT_CHANGED) != 0 @@ -93,6 +98,7 @@ public class JSyntaxTextArea extends RSyntaxTextArea { public static JSyntaxTextArea getInstance(int rows, int cols, boolean disableUndo) { try { JSyntaxTextArea jSyntaxTextArea = new JSyntaxTextArea(rows, cols, disableUndo); + jSyntaxTextArea.createPopupMenu(); JFactory.withDynamic(jSyntaxTextArea, JSyntaxTextArea::applyTheme); // Gutter styling is only applied if the text area is contained in a scroll pane. jSyntaxTextArea.addHierarchyListener(GUTTER_THEME_PATCHER); @@ -143,16 +149,53 @@ public String getText() { */ private static void applyTheme(JSyntaxTextArea jSyntaxTextArea) { final boolean isDarklafTheme = LookAndFeelCommand.isDarklafTheme(); - final Theme theme = isDarklafTheme ? new DarklafRSyntaxTheme(jSyntaxTextArea) : DEFAULT_THEME; + final boolean isFlatlafTheme = LookAndFeelCommand.isFlatlafTheme(); + final Theme theme = isDarklafTheme ? new DarklafRSyntaxTheme(jSyntaxTextArea) : (LookAndFeelCommand.isDark() ? DEFAULT_DARK_THEME : DEFAULT_THEME); + + // Calculate scaled font size + float scale = JMeterUIDefaults.INSTANCE.getScale(); + Font baseFont; + if (USER_FONT_FAMILY != null) { + baseFont = JMeterUIDefaults.createFont(USER_FONT_FAMILY, Font.PLAIN, USER_FONT_SIZE > 0 ? USER_FONT_SIZE : 12); + } else { + baseFont = JMeterUIDefaults.createFont(jSyntaxTextArea.getFont().getFamily(), Font.PLAIN, USER_FONT_SIZE > 0 ? USER_FONT_SIZE : 12); + } + if (Math.abs(scale - 1.0f) > 0.1) { + baseFont = baseFont.deriveFont(baseFont.getSize2D() * scale); + } + + // Apply theme first if (theme != null) { theme.apply(jSyntaxTextArea); - Font font = jSyntaxTextArea.getFont(); - float scale = JMeterUIDefaults.INSTANCE.getScale(); - if (Math.abs(scale - 1.0f) > 0.01) { - font = font.deriveFont(font.getSize2D() * scale); - jSyntaxTextArea.setFont(font); + } + + // Apply scaled font to all token types + SyntaxScheme scheme = jSyntaxTextArea.getSyntaxScheme(); + for (int i = 0; i < scheme.getStyleCount(); i++) { + Style style = scheme.getStyle(i); + if (style != null) { + if (style.font != null) { + // Keep the style (bold/italic) but use scaled size + style.font = style.font.deriveFont(baseFont.getSize2D()); + } else { + style.font = baseFont; + } } } + jSyntaxTextArea.setFont(baseFont); + jSyntaxTextArea.setSyntaxScheme(scheme); + jSyntaxTextArea.setFractionalFontMetricsEnabled(true); + + if (isFlatlafTheme) { + jSyntaxTextArea.setForeground(UIManager.getColor("TextArea.foreground")); + jSyntaxTextArea.setCaretColor(UIManager.getColor("TextArea.caretForeground")); + jSyntaxTextArea.setSelectionColor(UIManager.getColor("TextArea.selectionBackground")); + jSyntaxTextArea.setSelectedTextColor(UIManager.getColor("TextArea.selectionForeground")); + if (UIManager.getColor("Hyperlink.linkColor") != null) { + jSyntaxTextArea.setHyperlinkForeground(UIManager.getColor("Hyperlink.linkColor")); + } + } + if (!isDarklafTheme) { // Darklaf themes provide a custom background color for editors, so we don't overwrite it. Color color = UIManager.getColor("TextArea.background"); @@ -161,6 +204,7 @@ private static void applyTheme(JSyntaxTextArea jSyntaxTextArea) { jSyntaxTextArea.setBackground(color); } } + jSyntaxTextArea.revalidate(); } /** @@ -241,14 +285,32 @@ public JSyntaxTextArea(int rows, int cols, boolean disableUndo) { super.setAntiAliasingEnabled(true); super.setLineWrap(LINE_WRAP); super.setWrapStyleWord(WRAP_STYLE_WORD); + super.setMarkOccurrences(HIGHLIGHT_OCCURRENCES); + super.setTabSize(TAB_SIZE); this.disableUndo = disableUndo; + + // Add component listener to handle window resizing + addComponentListener(new java.awt.event.ComponentAdapter() { + @Override + public void componentResized(java.awt.event.ComponentEvent e) { + // Ensure text wraps to the new width + revalidate(); + repaint(); + } + }); + + int fontSize = USER_FONT_SIZE > 0 ? USER_FONT_SIZE : getFont().getSize(); + Font font = getFont(); if (USER_FONT_FAMILY != null) { - int fontSize = USER_FONT_SIZE > 0 ? USER_FONT_SIZE : getFont().getSize(); - setFont(JMeterUIDefaults.createFont(USER_FONT_FAMILY, Font.PLAIN, fontSize)); + font = JMeterUIDefaults.createFont(USER_FONT_FAMILY, Font.PLAIN, fontSize); + } else { + font = font.deriveFont(font.getStyle(), fontSize); + } + setFont(font); if (log.isDebugEnabled()) { log.debug("Font is set to: {}", getFont()); } - } + if(disableUndo) { TextComponentUI.uninstallUndo(this); // We need to do this to force recreation of undoManager which diff --git a/src/core/src/main/java/org/apache/jmeter/gui/util/TextAreaCellRenderer.java b/src/core/src/main/java/org/apache/jmeter/gui/util/TextAreaCellRenderer.java index cb8600a5d80..5e8131f98b6 100644 --- a/src/core/src/main/java/org/apache/jmeter/gui/util/TextAreaCellRenderer.java +++ b/src/core/src/main/java/org/apache/jmeter/gui/util/TextAreaCellRenderer.java @@ -21,8 +21,11 @@ import java.awt.Component; import javax.swing.JTable; +import javax.swing.UIManager; import javax.swing.table.TableCellRenderer; +import org.apache.jmeter.gui.action.LookAndFeelCommand; + public class TextAreaCellRenderer implements TableCellRenderer { private JSyntaxTextArea rend = createRenderer(""); //$NON-NLS-1$ @@ -36,13 +39,20 @@ public Component getTableCellRendererComponent(JTable table, Object value, } else { rend = createRenderer(""); //$NON-NLS-1$ } + if (hasFocus || isSelected) { - rend.setBackground(Color.blue); + if (LookAndFeelCommand.isFlatlafTheme()) { + rend.setBackground(UIManager.getColor("Table.selectionBackground")); + rend.setForeground(UIManager.getColor("Table.selectionForeground")); + } else { + rend.setBackground(Color.pink); rend.setForeground(Color.white); } + } if (table.getRowHeight(row) < getPreferredHeight()) { table.setRowHeight(row, getPreferredHeight()); } + return JTextScrollPane.getInstance(rend); } diff --git a/src/core/src/main/java/org/apache/jmeter/util/JMeterUtils.java b/src/core/src/main/java/org/apache/jmeter/util/JMeterUtils.java index 2a7c5b9c990..36ef025d4e8 100644 --- a/src/core/src/main/java/org/apache/jmeter/util/JMeterUtils.java +++ b/src/core/src/main/java/org/apache/jmeter/util/JMeterUtils.java @@ -1370,6 +1370,7 @@ public static String getDelimiter(String delimiterValue) { */ @API(since = "5.3", status = API.Status.DEPRECATED) public static void applyHiDPIOnFonts() { + log.info("Apply HiDPI on fonts: mode={}, scale={}", getHiDPIMode(), getHiDPIScaleFactor()); if (!getHiDPIMode()) { return; } diff --git a/src/jorphan/src/main/java/org/apache/jorphan/gui/JMeterUIDefaults.java b/src/jorphan/src/main/java/org/apache/jorphan/gui/JMeterUIDefaults.java index b8e98da783a..0d5e34592c7 100644 --- a/src/jorphan/src/main/java/org/apache/jorphan/gui/JMeterUIDefaults.java +++ b/src/jorphan/src/main/java/org/apache/jorphan/gui/JMeterUIDefaults.java @@ -103,11 +103,12 @@ private JMeterUIDefaults() { @API(since = "5.3", status = API.Status.INTERNAL) public void install() { DynamicStyle.onLaFChange(() -> { + log.info("LAF changed, updating JMeter-specific properties, scale: {}", scale); // $NON-NLS-1$ // We put JMeter-specific properties into getLookAndFeelDefaults, // so the properties are removed when LaF is changed UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - if (Math.abs(scale - 1.0f) > 0.01f) { + if (Math.abs(scale - 1.0f) > 0.1f) { scaleFonts(defaults); scaleIntProperties(defaults, scale); // We don't want to make controls extra big, so we damp the scaling factors @@ -181,10 +182,11 @@ private static void configureRowHeight(UIDefaults defaults, float scale, String Font f = d.getFont(font); float height; if (f == null) { - height = 16 * scale; + height = 14 * scale; } else { - Canvas c = new Canvas(); - height = c.getFontMetrics(f).getHeight(); + //Canvas c = new Canvas(); + //height = c.getFontMetrics(f).getHeight(); + height = f.getSize2D(); } // Set line height to be 1.3 of the font size. The number of completely made up, // 1.2 seems to be the minimal usable scale. 1.3 looks good. @@ -221,7 +223,7 @@ private void scaleFonts(UIDefaults defaults) { return; } } - // For other LaFs just update all the font resources. + // For other LAFs just update all the font resources. // Note: there might be derived fonts (e.g. javax.swing.UIDefaults.ActiveValue) // that compute their value based on another UIDefaults // So we update fonts in two loops: first we remember the actual fonts, then we update them diff --git a/xdocs/stylesheets/website-style.xsl b/xdocs/stylesheets/website-style.xsl index b12799e3e13..49438ed165e 100644 --- a/xdocs/stylesheets/website-style.xsl +++ b/xdocs/stylesheets/website-style.xsl @@ -38,7 +38,7 @@ - + diff --git a/xdocs/usermanual/hints_and_tips.xml b/xdocs/usermanual/hints_and_tips.xml index 070cea80273..e69b035aa3a 100644 --- a/xdocs/usermanual/hints_and_tips.xml +++ b/xdocs/usermanual/hints_and_tips.xml @@ -129,7 +129,7 @@ will match any component that contains test in searchable elements of the compon Additionally you can increase the font size of the text areas in some elements like JSR223 sampler by changing theses properties: jsyntaxtextarea.font.family - set to Hack to activate and to change the font and their size + change the font type, for example: Monospaced or Noto Mono or Inter Medium or other system/JDK supported font. jsyntaxtextarea.font.size set to a greater value, like 28 (Suggested value for HiDPI) diff --git a/xdocs/usermanual/properties_reference.xml b/xdocs/usermanual/properties_reference.xml index a40ae5027a6..60612180c03 100644 --- a/xdocs/usermanual/properties_reference.xml +++ b/xdocs/usermanual/properties_reference.xml @@ -1503,14 +1503,29 @@ JMETER-SERVER Set to zero to disable undo feature in JSyntaxTextArea. Defaults to: 50 + + Set to false to disable word highlight in JSyntaxTextArea. + Defaults to: true + + + Set to how many spaces on TAB character have. + Defaults to: 4 + Change the font on the (JSyntax) Text Areas. (Useful for HiDPI screens). - Defaults to empty value, which means platform default monospaced font + Defaults to empty value, which means platform default monospaced font. Change the size of the (JSyntax) Text Areas. Will be used only, when jsyntaxtextarea.font.family is set. Defaults to: -1 + + Change the default theme of the (JSyntax) Text Areas. + Defaults to: themes/default.xml + + Change the default dark theme of the (JSyntax) Text Areas. When LAF is + detected to be of 'dark' type this is also applied in (JSyntax) Text Areas. + Defaults to: themes/dark.xml Set this to false to disable the use of JSyntaxTextArea for the Console Logger panel.
Additionally you can increase the font size of the text areas in some elements like JSR223 sampler by changing theses properties:
jsyntaxtextarea.font.family
Hack
Monospaced
Noto Mono
Inter Medium
jsyntaxtextarea.font.size
28
50
true
4
-1
themes/default.xml
themes/dark.xml
false