feat(gui): add dynamic code editor theme from UI theme (PR #2471)
fix(gui): created dynamic theme for code editor (#1763)
This commit is contained in:
@@ -102,6 +102,7 @@ public class JadxSettings extends JadxCLIArgs {
|
||||
private boolean showHeapUsageBar = false;
|
||||
private boolean alwaysSelectOpened = false;
|
||||
private boolean useAlternativeFileDialog = false;
|
||||
private boolean useDynamicEditorTheme = false;
|
||||
|
||||
private Map<String, WindowLocation> windowPos = new HashMap<>();
|
||||
private int mainWindowExtendedState = JFrame.NORMAL;
|
||||
@@ -324,6 +325,14 @@ public class JadxSettings extends JadxCLIArgs {
|
||||
this.useAlternativeFileDialog = useAlternativeFileDialog;
|
||||
}
|
||||
|
||||
public boolean isUseDynamicEditorTheme() {
|
||||
return useDynamicEditorTheme;
|
||||
}
|
||||
|
||||
public void setUseDynamicEditorTheme(boolean useDynamicEditorTheme) {
|
||||
this.useDynamicEditorTheme = useDynamicEditorTheme;
|
||||
}
|
||||
|
||||
public String getExcludedPackages() {
|
||||
return excludedPackages;
|
||||
}
|
||||
|
||||
@@ -353,6 +353,7 @@ public class JadxSettingsWindow extends JDialog {
|
||||
|
||||
EditorTheme[] editorThemes = EditorTheme.getAllThemes();
|
||||
JComboBox<EditorTheme> themesCbx = new JComboBox<>(editorThemes);
|
||||
themesCbx.setEnabled(!settings.isUseDynamicEditorTheme());
|
||||
for (EditorTheme theme : editorThemes) {
|
||||
if (theme.getPath().equals(settings.getEditorThemePath())) {
|
||||
themesCbx.setSelectedItem(theme);
|
||||
@@ -366,6 +367,14 @@ public class JadxSettingsWindow extends JDialog {
|
||||
mainWindow.loadSettings();
|
||||
});
|
||||
|
||||
JCheckBox useDynamicEditorTheme = new JCheckBox();
|
||||
useDynamicEditorTheme.setSelected(settings.isUseDynamicEditorTheme());
|
||||
useDynamicEditorTheme.addItemListener(e -> {
|
||||
settings.setUseDynamicEditorTheme(e.getStateChange() == ItemEvent.SELECTED);
|
||||
mainWindow.loadSettings();
|
||||
themesCbx.setEnabled(!settings.isUseDynamicEditorTheme());
|
||||
});
|
||||
|
||||
JComboBox<String> lafCbx = new JComboBox<>(LafManager.getThemes());
|
||||
lafCbx.setSelectedItem(settings.getLafTheme());
|
||||
lafCbx.addActionListener(e -> {
|
||||
@@ -376,6 +385,7 @@ public class JadxSettingsWindow extends JDialog {
|
||||
SettingsGroup group = new SettingsGroup(NLS.str("preferences.appearance"));
|
||||
group.addRow(NLS.str("preferences.language"), languageCbx);
|
||||
group.addRow(NLS.str("preferences.laf_theme"), lafCbx);
|
||||
group.addRow(NLS.str("preferences.dynamic_editor_theme"), useDynamicEditorTheme);
|
||||
group.addRow(NLS.str("preferences.theme"), themesCbx);
|
||||
JLabel fontLabel = group.addRow(getFontLabelStr(), fontBtn);
|
||||
JLabel smaliFontLabel = group.addRow(getSmaliFontLabelStr(), smaliFontBtn);
|
||||
|
||||
@@ -31,9 +31,11 @@ import javax.swing.text.DefaultCaret;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.AbstractTokenMakerFactory;
|
||||
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
|
||||
import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities;
|
||||
import org.fife.ui.rsyntaxtextarea.Token;
|
||||
import org.fife.ui.rsyntaxtextarea.TokenMakerFactory;
|
||||
import org.fife.ui.rsyntaxtextarea.TokenTypes;
|
||||
import org.fife.ui.rtextarea.Gutter;
|
||||
import org.fife.ui.rtextarea.SearchContext;
|
||||
import org.fife.ui.rtextarea.SearchEngine;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -365,16 +367,25 @@ public abstract class AbstractCodeArea extends RSyntaxTextArea {
|
||||
RSyntaxTextArea area = new RSyntaxTextArea();
|
||||
area.setEditable(false);
|
||||
area.setCodeFoldingEnabled(false);
|
||||
area.setAntiAliasingEnabled(true);
|
||||
loadCommonSettings(mainWindow, area);
|
||||
return area;
|
||||
}
|
||||
|
||||
public static void loadCommonSettings(MainWindow mainWindow, RSyntaxTextArea area) {
|
||||
area.setAntiAliasingEnabled(true);
|
||||
mainWindow.getEditorTheme().apply(area);
|
||||
|
||||
JadxSettings settings = mainWindow.getSettings();
|
||||
if (settings.isUseDynamicEditorTheme()) {
|
||||
DynamicCoreAreaTheme.applyCustomTheme(area);
|
||||
} else {
|
||||
mainWindow.getEditorTheme().apply(area);
|
||||
}
|
||||
|
||||
area.setFont(settings.getFont());
|
||||
Gutter gutter = RSyntaxUtilities.getGutter(area);
|
||||
if (gutter != null) {
|
||||
gutter.setLineNumberFont(settings.getFont());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void loadSettings() {
|
||||
@@ -509,6 +520,14 @@ public abstract class AbstractCodeArea extends RSyntaxTextArea {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUI() {
|
||||
super.updateUI();
|
||||
if (contentPanel != null && contentPanel.getMainWindow() != null) {
|
||||
loadSettings();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
try {
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
package jadx.gui.ui.codearea;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import javax.swing.UIManager;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
|
||||
import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities;
|
||||
import org.fife.ui.rsyntaxtextarea.SyntaxScheme;
|
||||
import org.fife.ui.rsyntaxtextarea.Token;
|
||||
import org.fife.ui.rtextarea.Gutter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class DynamicCoreAreaTheme {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DynamicCoreAreaTheme.class);
|
||||
|
||||
public static void applyCustomTheme(RSyntaxTextArea textArea) {
|
||||
// Get the current theme's background color from UIManager
|
||||
Color themeBackground = UIManager.getColor("Panel.background");
|
||||
|
||||
boolean isDarkTheme = isDarkTheme(themeBackground);
|
||||
|
||||
SyntaxScheme scheme = textArea.getSyntaxScheme();
|
||||
|
||||
// Background colors based on the theme
|
||||
Color editorBackground = isDarkTheme ? themeBackground : Color.WHITE; // Use white for light theme
|
||||
Color lineHighlight = isDarkTheme
|
||||
? adjustBrightness(themeBackground, 1.2f)
|
||||
: Color.decode("#EBECF0"); // Light gray for light theme
|
||||
Color lineNumberForeground = UIManager.getColor("Label.foreground");
|
||||
|
||||
// Add these lines after setting the background colors
|
||||
Color selectionColor = isDarkTheme
|
||||
? new Color(51, 153, 255, 90) // Semi-transparent blue for dark theme
|
||||
: new Color(51, 153, 255, 50); // Lighter blue for light theme
|
||||
|
||||
Color markAllHighlightColor = isDarkTheme ? Color.decode("#32593D") : Color.decode("#ffc800");
|
||||
Color markOccurrencesColor = isDarkTheme ? Color.decode("#52503A") : Color.decode("#FCE8F4");
|
||||
Color matchedBracketBackground = isDarkTheme ? adjustBrightness(Color.decode("#3B514D"), 1.2f) : Color.decode("#93D9D9");
|
||||
Color gutterBorderColor = UIManager.getColor("Separator.foreground");
|
||||
Color hyperlinkColor = UIManager.getColor("EditorPane.selectionBackground");
|
||||
|
||||
// Set the syntax colors for the theme
|
||||
if (isDarkTheme) {
|
||||
Color dataTypeColor = Color.decode("#4EC9B0");
|
||||
Color booleanColor = Color.decode("#569CD6");
|
||||
scheme.getStyle(Token.COMMENT_EOL).foreground = Color.decode("#57A64A");
|
||||
scheme.getStyle(Token.COMMENT_MULTILINE).foreground = Color.decode("#57A64A");
|
||||
scheme.getStyle(Token.COMMENT_DOCUMENTATION).foreground = Color.decode("#57A64A");
|
||||
scheme.getStyle(Token.COMMENT_KEYWORD).foreground = Color.decode("#57A64A");
|
||||
scheme.getStyle(Token.COMMENT_MARKUP).foreground = Color.decode("#57A64A");
|
||||
scheme.getStyle(Token.RESERVED_WORD).foreground = Color.decode("#569CD6");
|
||||
scheme.getStyle(Token.RESERVED_WORD_2).foreground = dataTypeColor;
|
||||
scheme.getStyle(Token.FUNCTION).foreground = Color.decode("#DCDCAA");
|
||||
scheme.getStyle(Token.LITERAL_NUMBER_DECIMAL_INT).foreground = Color.decode("#D7BA7D");
|
||||
scheme.getStyle(Token.LITERAL_NUMBER_FLOAT).foreground = Color.decode("#D7BA7D");
|
||||
scheme.getStyle(Token.LITERAL_NUMBER_HEXADECIMAL).foreground = Color.decode("#D7BA7D");
|
||||
scheme.getStyle(Token.LITERAL_BOOLEAN).foreground = booleanColor;
|
||||
scheme.getStyle(Token.LITERAL_CHAR).foreground = Color.decode("#CE9178");
|
||||
scheme.getStyle(Token.LITERAL_STRING_DOUBLE_QUOTE).foreground = Color.decode("#CE9178");
|
||||
scheme.getStyle(Token.DATA_TYPE).foreground = dataTypeColor;
|
||||
scheme.getStyle(Token.OPERATOR).foreground = Color.WHITE;
|
||||
scheme.getStyle(Token.SEPARATOR).foreground = Color.WHITE;
|
||||
scheme.getStyle(Token.IDENTIFIER).foreground = Color.decode("#9CDCFE");
|
||||
// XML-specific colors for dark theme
|
||||
scheme.getStyle(Token.MARKUP_TAG_DELIMITER).foreground = Color.decode("#808080"); // Gray for < > /
|
||||
scheme.getStyle(Token.MARKUP_TAG_NAME).foreground = Color.decode("#569CD6"); // Blue for tag names
|
||||
scheme.getStyle(Token.MARKUP_TAG_ATTRIBUTE).foreground = Color.decode("#9CDCFE"); // Light blue for attributes
|
||||
scheme.getStyle(Token.MARKUP_TAG_ATTRIBUTE_VALUE).foreground = Color.decode("#CE9178"); // Orange for values
|
||||
} else {
|
||||
Color booleanColor = Color.decode("#0451A5");
|
||||
Color keyColor = Color.decode("#4A7A4F"); // Soft green for keys
|
||||
Color dataTypeColor = Color.decode("#267F99");
|
||||
scheme.getStyle(Token.COMMENT_EOL).foreground = Color.decode("#008000");
|
||||
scheme.getStyle(Token.COMMENT_MULTILINE).foreground = Color.decode("#008000");
|
||||
scheme.getStyle(Token.COMMENT_DOCUMENTATION).foreground = Color.decode("#008000");
|
||||
scheme.getStyle(Token.COMMENT_KEYWORD).foreground = Color.decode("#008000");
|
||||
scheme.getStyle(Token.COMMENT_MARKUP).foreground = Color.decode("#008000");
|
||||
scheme.getStyle(Token.RESERVED_WORD).foreground = Color.decode("#0000FF");
|
||||
scheme.getStyle(Token.RESERVED_WORD_2).foreground = dataTypeColor;
|
||||
scheme.getStyle(Token.FUNCTION).foreground = Color.decode("#795E26");
|
||||
scheme.getStyle(Token.ANNOTATION).foreground = Color.decode("#9E8809");
|
||||
scheme.getStyle(Token.LITERAL_NUMBER_DECIMAL_INT).foreground = Color.decode("#098658");
|
||||
scheme.getStyle(Token.LITERAL_NUMBER_FLOAT).foreground = Color.decode("#098658");
|
||||
scheme.getStyle(Token.LITERAL_NUMBER_HEXADECIMAL).foreground = Color.decode("#098658");
|
||||
scheme.getStyle(Token.LITERAL_BOOLEAN).foreground = booleanColor;
|
||||
scheme.getStyle(Token.LITERAL_CHAR).foreground = Color.decode("#067d17");
|
||||
scheme.getStyle(Token.LITERAL_STRING_DOUBLE_QUOTE).foreground = Color.decode("#067d17"); // Soft blue for values
|
||||
scheme.getStyle(Token.DATA_TYPE).foreground = dataTypeColor;
|
||||
scheme.getStyle(Token.OPERATOR).foreground = Color.decode("#333333");
|
||||
scheme.getStyle(Token.SEPARATOR).foreground = Color.decode("#333333");
|
||||
scheme.getStyle(Token.IDENTIFIER).foreground = Color.decode("#001080");
|
||||
// XML-specific colors for light theme
|
||||
scheme.getStyle(Token.MARKUP_TAG_DELIMITER).foreground = Color.decode("#800000"); // Dark red for < > /
|
||||
scheme.getStyle(Token.MARKUP_TAG_NAME).foreground = keyColor; // Soft green for tag names (keys)
|
||||
scheme.getStyle(Token.MARKUP_TAG_ATTRIBUTE).foreground = Color.decode("#FF0000"); // Red for attributes
|
||||
scheme.getStyle(Token.MARKUP_TAG_ATTRIBUTE_VALUE).foreground = Color.decode("#0000FF"); // Blue for values
|
||||
}
|
||||
|
||||
// Apply background colors
|
||||
textArea.setBackground(editorBackground);
|
||||
textArea.setCaretColor(UIManager.getColor("EditorPane.caretForeground"));
|
||||
textArea.setSelectionColor(selectionColor);
|
||||
textArea.setCurrentLineHighlightColor(lineHighlight);
|
||||
textArea.setMarkAllHighlightColor(markAllHighlightColor);
|
||||
textArea.setMarkOccurrencesColor(markOccurrencesColor);
|
||||
textArea.setHyperlinkForeground(hyperlinkColor);
|
||||
textArea.setMatchedBracketBGColor(matchedBracketBackground);
|
||||
textArea.setMatchedBracketBorderColor(lineNumberForeground);
|
||||
|
||||
textArea.setPaintMatchedBracketPair(true);
|
||||
textArea.setAnimateBracketMatching(false);
|
||||
textArea.setFadeCurrentLineHighlight(true);
|
||||
// Reset gutter colors directly to ensure the change applies
|
||||
Gutter gutter = RSyntaxUtilities.getGutter(textArea);
|
||||
if (gutter != null) {
|
||||
gutter.setBackground(editorBackground);
|
||||
gutter.setBorderColor(gutterBorderColor);
|
||||
gutter.setLineNumberColor(lineNumberForeground);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isDarkTheme(Color background) {
|
||||
double brightness = (background.getRed() * 0.299
|
||||
+ background.getGreen() * 0.587
|
||||
+ background.getBlue() * 0.114) / 255;
|
||||
return brightness < 0.5;
|
||||
}
|
||||
|
||||
private static Color adjustBrightness(Color color, float factor) {
|
||||
float[] hsb = Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), null);
|
||||
hsb[2] = Math.min(1.0f, hsb[2] * factor); // Adjust brightness
|
||||
return Color.getHSBColor(hsb[0], hsb[1], hsb[2]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -176,8 +176,10 @@ public final class SmaliArea extends AbstractCodeArea {
|
||||
private class NormalModel extends SmaliModel {
|
||||
|
||||
public NormalModel() {
|
||||
Theme theme = getContentPanel().getMainWindow().getEditorTheme();
|
||||
setSyntaxScheme(theme.scheme);
|
||||
if (!getContentPanel().getMainWindow().getSettings().isUseDynamicEditorTheme()) {
|
||||
Theme theme = getContentPanel().getMainWindow().getEditorTheme();
|
||||
setSyntaxScheme(theme.scheme);
|
||||
}
|
||||
setSyntaxEditingStyle(SYNTAX_STYLE_SMALI);
|
||||
}
|
||||
|
||||
@@ -327,6 +329,9 @@ public final class SmaliArea extends AbstractCodeArea {
|
||||
}
|
||||
|
||||
public boolean refreshTheme() {
|
||||
if (getContentPanel().getMainWindow().getSettings().isUseDynamicEditorTheme()) {
|
||||
return false;
|
||||
}
|
||||
Theme theme = getContentPanel().getMainWindow().getEditorTheme();
|
||||
boolean refresh = theme != curTheme;
|
||||
if (refresh) {
|
||||
@@ -337,7 +342,7 @@ public final class SmaliArea extends AbstractCodeArea {
|
||||
}
|
||||
|
||||
private void updateTheme() {
|
||||
Style[] mainStyles = curTheme.scheme.getStyles();
|
||||
Style[] mainStyles = getSyntaxScheme().getStyles();
|
||||
Style[] styles = new Style[mainStyles.length];
|
||||
for (int i = 0; i < mainStyles.length; i++) {
|
||||
Style mainStyle = mainStyles[i];
|
||||
|
||||
@@ -242,6 +242,7 @@ preferences.integerFormat=Ganzzahlformat
|
||||
preferences.font=Schriftart ändern
|
||||
preferences.smali_font=Monospaced-Schriftart (Smali/Hex)
|
||||
preferences.laf_theme=Thema
|
||||
#preferences.dynamic_editor_theme=Use dynamic editor theme
|
||||
preferences.theme=Thema ändern
|
||||
preferences.start_jobs=Autom. Hintergrunddekompilierung starten
|
||||
preferences.select_font=Ändern
|
||||
|
||||
@@ -242,6 +242,7 @@ preferences.integerFormat=Integer format
|
||||
preferences.font=Editor font
|
||||
preferences.smali_font=Monospaced font (Smali/Hex)
|
||||
preferences.laf_theme=Theme
|
||||
preferences.dynamic_editor_theme=Use dynamic editor theme
|
||||
preferences.theme=Editor theme
|
||||
preferences.start_jobs=Auto start background decompilation
|
||||
preferences.select_font=Change
|
||||
|
||||
@@ -242,6 +242,7 @@ preferences.raw_cfg=Generate RAW CFG graphs
|
||||
preferences.font=Fuente del editor
|
||||
#preferences.smali_font=Monospaced font (Smali/Hex)
|
||||
#preferences.laf_theme=Theme
|
||||
#preferences.dynamic_editor_theme=Use dynamic editor theme
|
||||
preferences.theme=Tema del editor
|
||||
preferences.start_jobs=Inicio autom. descompilación de fondo
|
||||
preferences.select_font=Seleccionar
|
||||
|
||||
@@ -242,6 +242,7 @@ preferences.integerFormat=Format bilangan bulat
|
||||
preferences.font=Font editor
|
||||
preferences.smali_font=Font monospasi (Smali/Hex)
|
||||
preferences.laf_theme=Tema
|
||||
#preferences.dynamic_editor_theme=Use dynamic editor theme
|
||||
preferences.theme=Tema editor
|
||||
preferences.start_jobs=Deskompilasi latar belakang otomatis
|
||||
preferences.select_font=Ubah
|
||||
|
||||
@@ -242,6 +242,7 @@ preferences.raw_cfg=RAW CFG 그래프 생성
|
||||
preferences.font=에디터 글씨체
|
||||
#preferences.smali_font=Monospaced font (Smali/Hex)
|
||||
preferences.laf_theme=테마
|
||||
#preferences.dynamic_editor_theme=Use dynamic editor theme
|
||||
preferences.theme=에디터 테마
|
||||
preferences.start_jobs=백그라운드에서 디컴파일 자동 시작
|
||||
preferences.select_font=변경
|
||||
|
||||
@@ -242,6 +242,7 @@ preferences.raw_cfg=Gera gráficos CFG no formato RAW
|
||||
preferences.font=Fonte do editor
|
||||
#preferences.smali_font=Monospaced font (Smali/Hex)
|
||||
preferences.laf_theme=Tema
|
||||
#preferences.dynamic_editor_theme=Use dynamic editor theme
|
||||
preferences.theme=Tema do editor
|
||||
preferences.start_jobs=Inicializar descompilação automaticamente em segundo-plano
|
||||
preferences.select_font=Alterar
|
||||
|
||||
@@ -242,6 +242,7 @@ preferences.integerFormat=Формат чисел
|
||||
preferences.font=Шрифт редактора Java
|
||||
preferences.smali_font=Шрифт smali/HEX редактора
|
||||
preferences.laf_theme=Тема приложения
|
||||
#preferences.dynamic_editor_theme=Use dynamic editor theme
|
||||
preferences.theme=Тема редактора
|
||||
preferences.start_jobs=Автоматическая декомпиляция
|
||||
preferences.select_font=Изменить
|
||||
|
||||
@@ -242,6 +242,7 @@ preferences.integerFormat=数值格式化
|
||||
preferences.font=编辑器字体
|
||||
preferences.smali_font=等宽字体 (Smali/Hex)
|
||||
preferences.laf_theme=主题
|
||||
#preferences.dynamic_editor_theme=Use dynamic editor theme
|
||||
preferences.theme=编辑器主题
|
||||
preferences.start_jobs=自动进行后台反编译
|
||||
preferences.select_font=修改
|
||||
|
||||
@@ -242,6 +242,7 @@ preferences.integerFormat=整數模式
|
||||
preferences.font=編輯器字型
|
||||
preferences.smali_font=等寬字型 (Smali/Hex)
|
||||
preferences.laf_theme=主題
|
||||
#preferences.dynamic_editor_theme=Use dynamic editor theme
|
||||
preferences.theme=編輯器主題
|
||||
preferences.start_jobs=自動開始背景反編譯
|
||||
preferences.select_font=變更
|
||||
|
||||
Reference in New Issue
Block a user