Skip to content

Commit b760352

Browse files
committed
feat: gui improvements
1 parent 9bd39e7 commit b760352

File tree

3 files changed

+267
-17
lines changed

3 files changed

+267
-17
lines changed

dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/gui/ConfigPanel.java

Lines changed: 190 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,20 @@
33
import javax.swing.*;
44
import java.awt.*;
55
import java.io.File;
6+
import java.io.IOException;
67

78
import dev.skidfuscator.jvm.Jvm;
89
import dev.skidfuscator.obfuscator.gui.autosave.AutoSaveDocumentListener;
910
import dev.skidfuscator.obfuscator.gui.config.SkidfuscatorConfig;
11+
import dev.skidfuscator.obfuscator.util.JdkDownloader;
12+
1013
import javax.swing.*;
1114
import java.awt.*;
1215
import java.io.File;
1316
import java.awt.event.WindowAdapter;
1417
import java.awt.event.WindowEvent;
18+
import javax.swing.event.DocumentEvent;
19+
import javax.swing.event.DocumentListener;
1520

1621
public class ConfigPanel extends JPanel {
1722
private final JTextField inputField;
@@ -36,55 +41,230 @@ public ConfigPanel() {
3641
add(new JLabel("Input JAR:"), gbc);
3742
gbc.gridx = 1;
3843
inputField = new JTextField(30);
44+
JLabel inputCheck = new JLabel("✗");
45+
inputCheck.setForeground(new Color(255, 65, 54));
46+
DocumentListener inputListener = new DocumentListener() {
47+
private void updateCheck() {
48+
boolean valid = new File(inputField.getText()).exists();
49+
inputCheck.setText(valid ? "✓" : "✗");
50+
inputCheck.setForeground(valid ? new Color(46, 204, 64) : new Color(255, 65, 54));
51+
52+
if (!valid) {
53+
StringBuilder tooltip = new StringBuilder("<html><body style='width: 250px; padding: 3px; background-color: #FFF3CD; border: 2px solid #FFE69C; border-radius: 4px'>");
54+
tooltip.append("<div style='color: #856404; font-weight: bold; margin-bottom: 5px'>⚠ Warning: Invalid Input Configuration</div>");
55+
tooltip.append("<div style='color: #664D03; margin: 3px 0'>Input file does not exist</div>");
56+
tooltip.append("</body></html>");
57+
58+
ToolTipManager.sharedInstance().setInitialDelay(0);
59+
ToolTipManager.sharedInstance().setDismissDelay(10000);
60+
inputField.setToolTipText(tooltip.toString());
61+
} else {
62+
inputField.setToolTipText(null);
63+
}
64+
}
65+
public void insertUpdate(DocumentEvent e) { updateCheck(); }
66+
public void removeUpdate(DocumentEvent e) { updateCheck(); }
67+
public void changedUpdate(DocumentEvent e) { updateCheck(); }
68+
};
69+
inputField.getDocument().addDocumentListener(inputListener);
3970
if (config.getLastInputPath() != null) {
4071
inputField.setText(config.getLastInputPath());
72+
inputListener.insertUpdate(null); // Trigger initial validation
4173
}
4274
add(inputField, gbc);
4375
gbc.gridx = 2;
44-
add(createBrowseButton(inputField, false), gbc);
76+
JPanel inputButtonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 0));
77+
inputButtonPanel.setPreferredSize(new Dimension(150, 30));
78+
JButton inputBrowseButton = createBrowseButton(inputField, false);
79+
inputButtonPanel.add(inputBrowseButton);
80+
inputButtonPanel.add(Box.createHorizontalGlue()); // Push label to right
81+
inputButtonPanel.add(inputCheck);
82+
add(inputButtonPanel, gbc);
4583

4684
// Output file
4785
gbc.gridx = 0; gbc.gridy = 1;
4886
add(new JLabel("Output JAR:"), gbc);
4987
gbc.gridx = 1;
5088
outputField = new JTextField(30);
89+
JLabel outputCheck = new JLabel("✗");
90+
outputCheck.setForeground(new Color(255, 65, 54));
91+
DocumentListener outputListener = new DocumentListener() {
92+
private void updateCheck() {
93+
final String output = outputField.getText();
94+
File parent = new File(output).getParentFile();
95+
96+
// [condition] must be valid file extension
97+
final boolean validEnd = output.endsWith(".jar")
98+
|| output.endsWith(".apk")
99+
|| output.endsWith(".dex");
100+
101+
// [condition] must not be input
102+
final boolean validInput = !inputField.getText().equals(output);
103+
104+
boolean valid = parent != null && parent.exists() && validEnd && validInput;
105+
outputCheck.setText(valid ? "✓" : "✗");
106+
outputCheck.setForeground(valid ? new Color(46, 204, 64) : new Color(255, 65, 54));
107+
// Set tooltip explaining validation failure
108+
StringBuilder tooltip = new StringBuilder("<html><body style='width: 250px; padding: 3px; background-color: #FFF3CD; border: 2px solid #FFE69C; border-radius: 4px'>");
109+
tooltip.append("<div style='color: #856404; font-weight: bold; margin-bottom: 5px'>⚠ Warning: Invalid Output Configuration</div>");
110+
111+
if (parent == null || !parent.exists()) {
112+
tooltip.append("<div style='color: #664D03; margin: 3px 0'>Output directory does not exist</div>");
113+
}
114+
if (!validEnd) {
115+
tooltip.append("<div style='color: #664D03; margin: 3px 0'>File must end with .jar, .apk or .dex</div>");
116+
}
117+
if (!validInput) {
118+
tooltip.append("<div style='color: #664D03; margin: 3px 0'>Output file cannot be the same as input file</div>");
119+
}
120+
tooltip.append("</body></html>");
121+
122+
if (!valid) {
123+
ToolTipManager.sharedInstance().setInitialDelay(0);
124+
ToolTipManager.sharedInstance().setDismissDelay(10000);
125+
126+
outputField.setToolTipText(tooltip.toString());
127+
} else {
128+
outputCheck.setToolTipText(null);
129+
}
130+
}
131+
public void insertUpdate(DocumentEvent e) { updateCheck(); }
132+
public void removeUpdate(DocumentEvent e) { updateCheck(); }
133+
public void changedUpdate(DocumentEvent e) { updateCheck(); }
134+
};
135+
outputField.getDocument().addDocumentListener(outputListener);
51136
if (config.getLastOutputPath() != null) {
52137
outputField.setText(config.getLastOutputPath());
138+
outputListener.insertUpdate(null);
53139
} else if (config.getLastInputPath() != null) {
54140
outputField.setText(config.getLastInputPath().replace(".jar", "-obf.jar"));
141+
outputListener.insertUpdate(null);
55142
}
56143
add(outputField, gbc);
57144
gbc.gridx = 2;
58-
add(createBrowseButton(outputField, false), gbc);
145+
JPanel outputButtonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 0));
146+
outputButtonPanel.setPreferredSize(new Dimension(150, 30));
147+
JButton outputBrowseButton = createBrowseButton(outputField, false);
148+
outputButtonPanel.add(outputBrowseButton);
149+
outputButtonPanel.add(Box.createHorizontalGlue());
150+
outputButtonPanel.add(outputCheck);
151+
add(outputButtonPanel, gbc);
59152

60153
// Libraries
61154
gbc.gridx = 0; gbc.gridy = 2;
62155
add(new JLabel("Libraries:"), gbc);
63156
gbc.gridx = 1;
64157
libsField = new JTextField(30);
158+
JLabel libsCheck = new JLabel("✗");
159+
libsCheck.setForeground(new Color(255, 65, 54));
160+
DocumentListener libsListener = new DocumentListener() {
161+
private void updateCheck() {
162+
boolean valid;
163+
if (libsField.getText().isEmpty()) {
164+
valid = true; // Empty is valid
165+
// Set orange dot for pending state
166+
libsCheck.setText("●");
167+
libsCheck.setForeground(new Color(255, 140, 0)); // Orange color
168+
return;
169+
} else {
170+
File dir = new File(libsField.getText());
171+
valid = dir.exists() && dir.isDirectory();
172+
}
173+
libsCheck.setText(valid ? "✓" : "✗");
174+
libsCheck.setForeground(valid ? new Color(46, 204, 64) : new Color(255, 65, 54));
175+
}
176+
public void insertUpdate(DocumentEvent e) { updateCheck(); }
177+
public void removeUpdate(DocumentEvent e) { updateCheck(); }
178+
public void changedUpdate(DocumentEvent e) { updateCheck(); }
179+
};
180+
libsField.getDocument().addDocumentListener(libsListener);
65181
if (config.getLastLibsPath() != null) {
66182
libsField.setText(config.getLastLibsPath());
183+
libsListener.insertUpdate(null);
67184
}
68185
add(libsField, gbc);
69186
gbc.gridx = 2;
70-
add(createBrowseButton(libsField, true), gbc);
187+
JPanel libsButtonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 0));
188+
libsButtonPanel.setPreferredSize(new Dimension(150, 30));
189+
JButton libsBrowseButton = createBrowseButton(libsField, true);
190+
libsButtonPanel.add(libsBrowseButton);
191+
libsButtonPanel.add(Box.createHorizontalGlue());
192+
libsButtonPanel.add(libsCheck);
193+
add(libsButtonPanel, gbc);
71194

72195
// Runtime
73196
gbc.gridx = 0; gbc.gridy = 3;
74197
add(new JLabel("Runtime:"), gbc);
75198
gbc.gridx = 1;
76199
runtimeField = new JTextField(30);
77-
if (config.getLastRuntimePath() != null) {
78-
if (config.getLastRuntimePath().isEmpty()) {
79-
runtimeField.setText(Jvm.getLibsPath());
80-
runtimeField.setEnabled(false);
81-
} else {
82-
runtimeField.setText(config.getLastRuntimePath());
200+
201+
// Check if JDK was previously downloaded
202+
try {
203+
String jmodPath = JdkDownloader.getCachedJmodPath();
204+
runtimeField.setText(jmodPath);
205+
runtimeField.setEnabled(!JdkDownloader.isJdkDownloaded());
206+
} catch (IOException e) {
207+
// Fallback to config
208+
if (config.getLastRuntimePath() != null) {
209+
if (config.getLastRuntimePath().isEmpty()) {
210+
runtimeField.setText(Jvm.getLibsPath());
211+
runtimeField.setEnabled(false);
212+
} else {
213+
runtimeField.setText(config.getLastRuntimePath());
214+
}
83215
}
84216
}
217+
85218
add(runtimeField, gbc);
219+
220+
// Add download button next to browse button
86221
gbc.gridx = 2;
87-
add(createBrowseButton(runtimeField, false), gbc);
222+
JPanel runtimeButtonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 0));
223+
JButton downloadButton = new JButton("Download JDK");
224+
225+
// Set initial button state based on JDK download status
226+
if (JdkDownloader.isJdkDownloaded()) {
227+
downloadButton.setText("Downloaded");
228+
downloadButton.setEnabled(false);
229+
}
230+
231+
downloadButton.addActionListener(e -> {
232+
downloadButton.setEnabled(false);
233+
downloadButton.setText("Downloading...");
234+
235+
SwingWorker<String, Void> worker = new SwingWorker<>() {
236+
@Override
237+
protected String doInBackground() throws Exception {
238+
return JdkDownloader.getJmodPath();
239+
}
240+
241+
@Override
242+
protected void done() {
243+
try {
244+
String path = get();
245+
runtimeField.setText(path);
246+
runtimeField.setEnabled(false);
247+
downloadButton.setText("Downloaded");
248+
downloadButton.setEnabled(false);
249+
} catch (Exception ex) {
250+
JOptionPane.showMessageDialog(
251+
ConfigPanel.this,
252+
"Failed to download JDK: " + ex.getMessage(),
253+
"Download Error",
254+
JOptionPane.ERROR_MESSAGE
255+
);
256+
downloadButton.setText("Download JDK");
257+
downloadButton.setEnabled(true);
258+
}
259+
}
260+
};
261+
worker.execute();
262+
});
263+
runtimeButtonPanel.add(downloadButton);
264+
265+
// removing for now
266+
//runtimeButtonPanel.add(createBrowseButton(runtimeField, false));
267+
add(runtimeButtonPanel, gbc);
88268

89269
// Checkboxes
90270
JPanel checkboxPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));

dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/gui/MainFrame.java

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,29 +42,47 @@ public void updateUI() {
4242
@Override
4343
protected Insets getTabAreaInsets(int tabPlacement) {
4444
Insets insets = super.getTabAreaInsets(tabPlacement);
45-
return new Insets(140, insets.left, insets.bottom, insets.right);
45+
return new Insets(220, insets.left, insets.bottom, insets.right);
4646
}
4747

4848
@Override
4949
protected int calculateTabAreaWidth(int tabPlacement, int horizRunCount, int maxTabWidth) {
50-
return 150;
50+
return 160;
5151
}
5252

5353
@Override
5454
protected int calculateTabWidth(int tabPlacement, int tabIndex, FontMetrics metrics) {
5555
// Make tabs fill the entire width of the tab area
56-
return 150;
56+
return 160;
5757
}
5858

5959
@Override
6060
protected void paintTabArea(Graphics g, int tabPlacement, int selectedIndex) {
6161
try {
62+
// Draw logo
6263
InputStream logoStream = getClass().getResourceAsStream("/images/logo.png");
6364
if (logoStream != null) {
6465
Image logo = ImageIO.read(logoStream);
6566
Image scaledLogo = logo.getScaledInstance(150, 150, Image.SCALE_SMOOTH);
66-
g.drawImage(scaledLogo, 5, 0, null);
67+
g.drawImage(scaledLogo, 5, 10, null);
6768
}
69+
70+
// Draw separator line
71+
g.setColor(Color.DARK_GRAY);
72+
g.drawLine(5, 155, 155, 155);
73+
74+
// Draw version info
75+
g.setColor(new Color(200,190,220));
76+
g.setFont(new Font("Segoe UI", Font.BOLD, 11));
77+
g.drawString("Skidfuscator Community", 10, 175);
78+
g.setColor(new Color(130, 130, 130));
79+
g.setFont(new Font("Segoe UI", Font.ITALIC, 11));
80+
g.drawString("Build: 2023.1", 10, 190);
81+
82+
// Draw second separator
83+
g.setColor(Color.DARK_GRAY);
84+
g.drawLine(5, 205, 155, 205);
85+
6886
} catch (Exception e) {
6987
e.printStackTrace();
7088
}
@@ -227,7 +245,6 @@ public void startObfuscation() {
227245
.jmod(config.getRuntimePath().contains("jmods"))
228246
.debug(config.isDebugEnabled())
229247
.build();
230-
231248
// Start obfuscation in background
232249
startButton.setEnabled(false);
233250
SwingWorker<Void, String> worker = new SwingWorker() {
@@ -246,6 +263,33 @@ protected Void doInBackground() {
246263
@Override
247264
protected void done() {
248265
startButton.setEnabled(true);
266+
SwingUtilities.invokeLater(() -> {
267+
int option = JOptionPane.showOptionDialog(
268+
MainFrame.this,
269+
"Obfuscation completed successfully!",
270+
"Success",
271+
JOptionPane.YES_NO_OPTION,
272+
JOptionPane.INFORMATION_MESSAGE,
273+
UIManager.getIcon("OptionPane.informationIcon"),
274+
new Object[]{"OK", "Open Output Folder"},
275+
"OK"
276+
);
277+
278+
if (option == 1) {
279+
// Open output folder
280+
try {
281+
File outputFile = new File(configPanel.getOutputPath());
282+
Desktop.getDesktop().open(outputFile.getParentFile());
283+
} catch (Exception e) {
284+
JOptionPane.showMessageDialog(
285+
MainFrame.this,
286+
"Could not open output folder: " + e.getMessage(),
287+
"Error",
288+
JOptionPane.ERROR_MESSAGE
289+
);
290+
}
291+
}
292+
});
249293
}
250294
};
251295
worker.execute();

0 commit comments

Comments
 (0)