Update FlatLaf & RSyntaxArea dependencies; removed Darcula LAF; correctly apply LAF on RSyntaxArea components.

This commit is contained in:
KingRabbid 2025-10-04 00:16:09 +03:00
parent d3c489885e
commit 2f128aa8e1
5 changed files with 80 additions and 34 deletions

View File

@ -45,7 +45,7 @@ dependencies {
api("com.fasterxml.jackson.core:jackson-annotations:2.16.1") api("com.fasterxml.jackson.core:jackson-annotations:2.16.1")
api("com.fasterxml.jackson.core:jackson-core:2.16.1") api("com.fasterxml.jackson.core:jackson-core:2.16.1")
api("com.fasterxml.jackson.core:jackson-databind:2.16.1") api("com.fasterxml.jackson.core:jackson-databind:2.16.1")
api("com.fifesoft:rsyntaxtextarea:3.3.4") api("com.fifesoft:rsyntaxtextarea:3.6.0")
api("com.formdev:svgSalamander:1.1.4") api("com.formdev:svgSalamander:1.1.4")
api("com.github.ben-manes.caffeine:caffeine:2.9.3") api("com.github.ben-manes.caffeine:caffeine:2.9.3")
api("com.github.weisj:darklaf-core:2.7.3") api("com.github.weisj:darklaf-core:2.7.3")
@ -144,11 +144,16 @@ dependencies {
api("xml-apis:xml-apis:1.4.01") api("xml-apis:xml-apis:1.4.01")
api("xmlpull:xmlpull:1.1.3.1") api("xmlpull:xmlpull:1.1.3.1")
//FlatLaf: https://www.formdev.com/flatlaf/native-libraries/#gradle //FlatLaf: https://www.formdev.com/flatlaf/native-libraries/#gradle
val flatlafVersion = "3.5.2" val flatlafVersion = "3.6.1"
api("com.formdev:flatlaf:${flatlafVersion}" ) api("com.formdev:flatlaf:${flatlafVersion}" )
api("com.formdev:flatlaf:${flatlafVersion}:linux-x86_64@so") api("com.formdev:flatlaf:${flatlafVersion}:linux-x86_64@so")
api("com.formdev:flatlaf:${flatlafVersion}:macos-x86_64@dylib") api("com.formdev:flatlaf:${flatlafVersion}:macos-x86_64@dylib")
api("com.formdev:flatlaf:${flatlafVersion}:windows-x86_64@dll") api("com.formdev:flatlaf:${flatlafVersion}:windows-x86_64@dll")
api("com.formdev:flatlaf-intellij-themes:${flatlafVersion}") 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")
} }
} }

View File

@ -18,11 +18,19 @@
package org.apache.jmeter.gui.action; package org.apache.jmeter.gui.action;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.util.*; import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.prefs.Preferences; import java.util.prefs.Preferences;
import javax.swing.*; import javax.swing.JOptionPane;
import javax.swing.UIManager;
import org.apache.jmeter.gui.GuiPackage; import org.apache.jmeter.gui.GuiPackage;
import org.apache.jmeter.gui.util.JMeterMenuBar; import org.apache.jmeter.gui.util.JMeterMenuBar;
@ -32,9 +40,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.formdev.flatlaf.FlatLaf; import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.intellijthemes.*; import com.formdev.flatlaf.intellijthemes.FlatAllIJThemes;
import com.github.weisj.darklaf.LafManager; import com.github.weisj.darklaf.LafManager;
import com.github.weisj.darklaf.theme.DarculaTheme;
import com.github.weisj.darklaf.theme.Theme; import com.github.weisj.darklaf.theme.Theme;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
@ -103,7 +110,6 @@ public class LookAndFeelCommand extends AbstractAction {
if (System.getProperty("darklaf.treeRowPopup") == null) { if (System.getProperty("darklaf.treeRowPopup") == null) {
System.setProperty("darklaf.treeRowPopup", "false"); System.setProperty("darklaf.treeRowPopup", "false");
} }
UIManager.installLookAndFeel(JMeterMenuBar.DARCULA_LAF, JMeterMenuBar.DARCULA_LAF_CLASS);
//Add FlatLAF Themes //Add FlatLAF Themes
for (String flatLaf : new String[]{"FlatLightLaf", "FlatDarkLaf", "FlatIntelliJLaf", "FlatDarculaLaf"}) { for (String flatLaf : new String[]{"FlatLightLaf", "FlatDarkLaf", "FlatIntelliJLaf", "FlatDarculaLaf"}) {
@ -126,7 +132,7 @@ public class LookAndFeelCommand extends AbstractAction {
List<MenuItem> items = new ArrayList<>(); List<MenuItem> items = new ArrayList<>();
for (UIManager.LookAndFeelInfo laf : JMeterMenuBar.getAllLAFs()) { 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())); items.add(MenuItem.of(laf.getName(), laf.getClassName()));
} else { } else {
for (Theme theme : LafManager.getRegisteredThemes()) { for (Theme theme : LafManager.getRegisteredThemes()) {
@ -161,6 +167,8 @@ public class LookAndFeelCommand extends AbstractAction {
public static String getJMeterLaf(){ public static String getJMeterLaf(){
String laf = PREFS.get(USER_PREFS_KEY, null); String laf = PREFS.get(USER_PREFS_KEY, null);
if (laf != null) { if (laf != null) {
for (UIManager.LookAndFeelInfo lafInfo : JMeterMenuBar.getAllLAFs())
if (lafInfo.getName().equalsIgnoreCase(laf))
return checkLafName(laf); return checkLafName(laf);
} }
@ -176,10 +184,7 @@ public class LookAndFeelCommand extends AbstractAction {
if (laf != null) { if (laf != null) {
return checkLafName(laf); return checkLafName(laf);
} }
laf = JMeterUtils.getPropDefault(JMETER_LAF, JMeterMenuBar.DARCULA_LAF_CLASS);
if (laf != null) {
return checkLafName(laf);
}
return UIManager.getCrossPlatformLookAndFeelClassName(); return UIManager.getCrossPlatformLookAndFeelClassName();
} }
@ -194,12 +199,11 @@ public class LookAndFeelCommand extends AbstractAction {
} }
String jMeterLaf = getJMeterLaf(); String jMeterLaf = getJMeterLaf();
if (jMeterLaf.equals(JMeterMenuBar.DARCULA_LAF_CLASS)) { if (jMeterLaf != null) {
// Convert old Darcula to new Darklaf-Darcula LaF return MenuItem.of("default", jMeterLaf).command; // $NON-NLS-1$
return MenuItem.ofDarklafTheme(new DarculaTheme()).command; } else {
return MenuItem.of("default", UIManager.getCrossPlatformLookAndFeelClassName()).command; // $NON-NLS-1$
} }
return MenuItem.of("default", jMeterLaf).command; // $NON-NLS-1$
} }
// Check if LAF is a built-in one // Check if LAF is a built-in one

View File

@ -40,8 +40,6 @@ import javax.swing.MenuElement;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo; import javax.swing.UIManager.LookAndFeelInfo;
import com.formdev.flatlaf.intellijthemes.FlatAllIJThemes;
import org.apache.jmeter.gui.GuiPackage; import org.apache.jmeter.gui.GuiPackage;
import org.apache.jmeter.gui.action.ActionNames; import org.apache.jmeter.gui.action.ActionNames;
import org.apache.jmeter.gui.action.ActionRouter; import org.apache.jmeter.gui.action.ActionRouter;
@ -104,10 +102,8 @@ public class JMeterMenuBar extends JMenuBar implements LocaleChangeListener {
public static final String SYSTEM_LAF = "System"; // $NON-NLS-1$ 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 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 DARKLAF_LAF = "Darklaf"; // $NON-NLS-1$
public static final String FLAT_LAF = "FlatLaf"; // $NON-NLS-1$ public static final String FLAT_LAF = "FlatLaf"; // $NON-NLS-1$
public static final String DARCULA_LAF_CLASS = "com.bulenkov.darcula.DarculaLaf"; // $NON-NLS-1$
public static final String DARKLAF_LAF_CLASS = "com.github.weisj.darklaf.DarkLaf"; // $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 static final String FLATLAF_LAF_CLASS = "com.formdev.flatlaf.FlatLightLaf"; // $NON-NLS-1$

View File

@ -35,7 +35,9 @@ import org.apache.jorphan.gui.JFactory;
import org.apache.jorphan.gui.JMeterUIDefaults; import org.apache.jorphan.gui.JMeterUIDefaults;
import org.apache.jorphan.gui.ui.TextComponentUI; import org.apache.jorphan.gui.ui.TextComponentUI;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.Style;
import org.fife.ui.rsyntaxtextarea.SyntaxConstants; import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
import org.fife.ui.rsyntaxtextarea.SyntaxScheme;
import org.fife.ui.rsyntaxtextarea.Theme; import org.fife.ui.rsyntaxtextarea.Theme;
import org.fife.ui.rtextarea.RUndoManager; import org.fife.ui.rtextarea.RUndoManager;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -95,6 +97,7 @@ public class JSyntaxTextArea extends RSyntaxTextArea {
public static JSyntaxTextArea getInstance(int rows, int cols, boolean disableUndo) { public static JSyntaxTextArea getInstance(int rows, int cols, boolean disableUndo) {
try { try {
JSyntaxTextArea jSyntaxTextArea = new JSyntaxTextArea(rows, cols, disableUndo); JSyntaxTextArea jSyntaxTextArea = new JSyntaxTextArea(rows, cols, disableUndo);
jSyntaxTextArea.createPopupMenu();
JFactory.withDynamic(jSyntaxTextArea, JSyntaxTextArea::applyTheme); JFactory.withDynamic(jSyntaxTextArea, JSyntaxTextArea::applyTheme);
// Gutter styling is only applied if the text area is contained in a scroll pane. // Gutter styling is only applied if the text area is contained in a scroll pane.
jSyntaxTextArea.addHierarchyListener(GUTTER_THEME_PATCHER); jSyntaxTextArea.addHierarchyListener(GUTTER_THEME_PATCHER);
@ -148,25 +151,50 @@ public class JSyntaxTextArea extends RSyntaxTextArea {
final boolean isFlatlafTheme = LookAndFeelCommand.isFlatlafTheme(); final boolean isFlatlafTheme = LookAndFeelCommand.isFlatlafTheme();
final Theme theme = isDarklafTheme ? new DarklafRSyntaxTheme(jSyntaxTextArea) : (LookAndFeelCommand.isDark() ? DEFAULT_DARK_THEME : DEFAULT_THEME); 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 = jSyntaxTextArea.getFont();
}
if (Math.abs(scale - 1.0f) > 0.01) {
baseFont = baseFont.deriveFont(baseFont.getSize2D() * scale);
}
// Apply theme first
if (theme != null) {
theme.apply(jSyntaxTextArea);
}
// 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) { if (isFlatlafTheme) {
jSyntaxTextArea.setForeground(UIManager.getColor("TextArea.foreground")); jSyntaxTextArea.setForeground(UIManager.getColor("TextArea.foreground"));
jSyntaxTextArea.setCaretColor(UIManager.getColor("TextArea.caretForeground")); jSyntaxTextArea.setCaretColor(UIManager.getColor("TextArea.caretForeground"));
jSyntaxTextArea.setSelectionColor(UIManager.getColor("TextArea.selectionBackground")); jSyntaxTextArea.setSelectionColor(UIManager.getColor("TextArea.selectionBackground"));
jSyntaxTextArea.setSelectedTextColor(UIManager.getColor("TextArea.selectionForeground")); jSyntaxTextArea.setSelectedTextColor(UIManager.getColor("TextArea.selectionForeground"));
//not set in all themes if (UIManager.getColor("Hyperlink.linkColor") != null) {
if (UIManager.getColor("Hyperlink.linkColor") != null)
jSyntaxTextArea.setHyperlinkForeground(UIManager.getColor("Hyperlink.linkColor")); jSyntaxTextArea.setHyperlinkForeground(UIManager.getColor("Hyperlink.linkColor"));
} }
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);
} }
}
if (!isDarklafTheme) { if (!isDarklafTheme) {
// Darklaf themes provide a custom background color for editors, so we don't overwrite it. // Darklaf themes provide a custom background color for editors, so we don't overwrite it.
Color color = UIManager.getColor("TextArea.background"); Color color = UIManager.getColor("TextArea.background");
@ -258,6 +286,16 @@ public class JSyntaxTextArea extends RSyntaxTextArea {
super.setMarkOccurrences(HIGHLIGHT_OCCURRENCES); super.setMarkOccurrences(HIGHLIGHT_OCCURRENCES);
this.disableUndo = disableUndo; 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(); int fontSize = USER_FONT_SIZE > 0 ? USER_FONT_SIZE : getFont().getSize();
Font font = getFont(); Font font = getFont();
if (USER_FONT_FAMILY != null) { if (USER_FONT_FAMILY != null) {

View File

@ -103,11 +103,13 @@ public class JMeterUIDefaults {
@API(since = "5.3", status = API.Status.INTERNAL) @API(since = "5.3", status = API.Status.INTERNAL)
public void install() { public void install() {
DynamicStyle.onLaFChange(() -> { DynamicStyle.onLaFChange(() -> {
log.info("LAF changed, updating JMeter-specific properties"); // $NON-NLS-1$
// We put JMeter-specific properties into getLookAndFeelDefaults, // We put JMeter-specific properties into getLookAndFeelDefaults,
// so the properties are removed when LaF is changed // so the properties are removed when LaF is changed
UIDefaults defaults = UIManager.getLookAndFeelDefaults(); UIDefaults defaults = UIManager.getLookAndFeelDefaults();
if (Math.abs(scale - 1.0f) > 0.01f) { if (Math.abs(scale - 1.0f) > 0.01f) {
log.info("Applying scale factor: {}", scale); // $NON-NLS-1$
scaleFonts(defaults); scaleFonts(defaults);
scaleIntProperties(defaults, scale); scaleIntProperties(defaults, scale);
// We don't want to make controls extra big, so we damp the scaling factors // We don't want to make controls extra big, so we damp the scaling factors
@ -183,8 +185,9 @@ public class JMeterUIDefaults {
if (f == null) { if (f == null) {
height = 16 * scale; height = 16 * scale;
} else { } else {
Canvas c = new Canvas(); //Canvas c = new Canvas();
height = c.getFontMetrics(f).getHeight(); //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, // 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. // 1.2 seems to be the minimal usable scale. 1.3 looks good.