Files
DynamicWallpaperCreator/src/main/java/com/dynamicwallpaper/gui/MainWindow.java
2026-07-11 22:52:23 -04:00

277 lines
11 KiB
Java

package com.dynamicwallpaper.gui;
import com.dynamicwallpaper.core.NativeHelper;
import com.dynamicwallpaper.core.WallpaperBuilder;
import com.dynamicwallpaper.core.WallpaperInstaller;
import com.dynamicwallpaper.model.WallpaperImage;
import com.dynamicwallpaper.model.WallpaperMode;
import com.dynamicwallpaper.util.ImageValidation;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingWorker;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.io.File;
import java.util.List;
/** Top-level application window: mode switch, image editors, and the generate/install controls. */
public final class MainWindow extends JFrame {
private final NativeHelper nativeHelper = new NativeHelper();
private final WallpaperBuilder wallpaperBuilder = new WallpaperBuilder(nativeHelper);
private final JComboBox<WallpaperMode> modeSelector = new JComboBox<>(WallpaperMode.values());
private final CardLayout cardLayout = new CardLayout();
private final JPanel modePanel = new JPanel(cardLayout);
private final AppearancePanel appearancePanel = new AppearancePanel();
private final SolarPanel solarPanel = new SolarPanel();
private final JTextField outputPathField = new JTextField();
private final JSpinner qualitySpinner = new JSpinner(new SpinnerNumberModel(0.9, 0.1, 1.0, 0.05));
private final JButton generateButton = new JButton("Generate Wallpaper");
private final JButton setWallpaperButton = new JButton("Set as Desktop Picture");
private final JProgressBar progressBar = new JProgressBar();
private final JTextArea logArea = new JTextArea(8, 60);
private File lastGeneratedFile;
public MainWindow() {
super("Dynamic Wallpaper Creator");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new BorderLayout());
add(buildHeader(), BorderLayout.NORTH);
add(modePanel, BorderLayout.CENTER);
add(buildFooter(), BorderLayout.SOUTH);
modePanel.add(appearancePanel, WallpaperMode.APPEARANCE.name());
modePanel.add(solarPanel, WallpaperMode.SOLAR.name());
modeSelector.addActionListener(e -> cardLayout.show(modePanel, currentMode().name()));
setWallpaperButton.setEnabled(false);
setWallpaperButton.addActionListener(e -> setAsDesktopPicture());
generateButton.addActionListener(e -> generate());
setMinimumSize(new Dimension(760, 640));
pack();
setLocationRelativeTo(null);
this.setSize(new Dimension(1200,800));
this.setLocationRelativeTo(null); // Center on screen
}
private Component buildHeader() {
JPanel header = new JPanel(new BorderLayout(8, 8));
header.setBorder(BorderFactory.createEmptyBorder(12, 16, 0, 16));
JLabel title = new JLabel("Choose a wallpaper type:");
JPanel left = new JPanel(new FlowLayout(FlowLayout.LEFT));
left.add(title);
left.add(modeSelector);
JLabel modeDescription = new JLabel(currentMode().description());
modeSelector.addActionListener(e -> modeDescription.setText(currentMode().description()));
modeDescription.setEnabled(false);
header.add(left, BorderLayout.NORTH);
header.add(modeDescription, BorderLayout.SOUTH);
return header;
}
private Component buildFooter() {
JPanel footer = new JPanel();
footer.setLayout(new BorderLayout(8, 8));
footer.setBorder(BorderFactory.createEmptyBorder(8, 16, 16, 16));
JPanel outputRow = new JPanel(new BorderLayout(8, 8));
outputRow.add(new JLabel("Output file:"), BorderLayout.WEST);
outputPathField.setText(defaultOutputPath());
outputRow.add(outputPathField, BorderLayout.CENTER);
JButton browseButton = new JButton("Choose…");
browseButton.addActionListener(e -> chooseOutputPath());
outputRow.add(browseButton, BorderLayout.EAST);
JPanel controlsRow = new JPanel(new FlowLayout(FlowLayout.LEFT));
controlsRow.add(new JLabel("Quality:"));
qualitySpinner.setPreferredSize(new Dimension(70, qualitySpinner.getPreferredSize().height));
controlsRow.add(qualitySpinner);
controlsRow.add(generateButton);
controlsRow.add(setWallpaperButton);
JPanel top = new JPanel(new GridLayout(2, 1));
top.add(outputRow);
top.add(controlsRow);
logArea.setEditable(false);
logArea.setFont(new java.awt.Font(java.awt.Font.MONOSPACED, java.awt.Font.PLAIN, 12));
footer.add(top, BorderLayout.NORTH);
footer.add(progressBar, BorderLayout.CENTER);
footer.add(new JScrollPane(logArea), BorderLayout.SOUTH);
return footer;
}
private WallpaperMode currentMode() {
return (WallpaperMode) modeSelector.getSelectedItem();
}
private String defaultOutputPath() {
String home = System.getProperty("user.home");
return new File(home, "Desktop/MyDynamicWallpaper.heic").getAbsolutePath();
}
private void chooseOutputPath() {
JFileChooser chooser = new JFileChooser();
chooser.setDialogTitle("Save Dynamic Wallpaper As");
chooser.setFileFilter(new FileNameExtensionFilter("HEIC Image", "heic"));
chooser.setSelectedFile(new File(outputPathField.getText()));
if (chooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) {
File selected = chooser.getSelectedFile();
if (!selected.getName().toLowerCase().endsWith(".heic")) {
selected = new File(selected.getParentFile(), selected.getName() + ".heic");
}
outputPathField.setText(selected.getAbsolutePath());
}
}
private void generate() {
File output = new File(outputPathField.getText().trim());
if (output.getParentFile() == null || !output.getParentFile().isDirectory()) {
JOptionPane.showMessageDialog(this, "Choose a valid output location first.",
"Missing output location", JOptionPane.WARNING_MESSAGE);
return;
}
WallpaperMode mode = currentMode();
double quality = (Double) qualitySpinner.getValue();
if (mode == WallpaperMode.APPEARANCE) {
File light = appearancePanel.lightImage();
File dark = appearancePanel.darkImage();
if (light == null || dark == null) {
JOptionPane.showMessageDialog(this, "Choose both a light image and a dark image first.",
"Missing images", JOptionPane.WARNING_MESSAGE);
return;
}
String warning = ImageValidation.checkConsistentDimensions(List.of(light, dark));
if (warning != null && !confirmProceedDespiteWarning(warning)) {
return;
}
runGenerateJob(output, listener -> wallpaperBuilder.buildAppearance(light, dark, output, quality, listener));
} else {
List<WallpaperImage> images = solarPanel.images();
if (images.size() < 2) {
JOptionPane.showMessageDialog(this, "Add at least two images for a sun-position wallpaper.",
"Not enough images", JOptionPane.WARNING_MESSAGE);
return;
}
String warning = ImageValidation.checkConsistentDimensions(images.stream().map(WallpaperImage::file).toList());
if (warning != null && !confirmProceedDespiteWarning(warning)) {
return;
}
runGenerateJob(output, listener -> wallpaperBuilder.buildSolar(images, output, quality, listener));
}
}
private boolean confirmProceedDespiteWarning(String warning) {
int choice = JOptionPane.showConfirmDialog(this, warning + "\n\nContinue anyway?",
"Image sizes differ", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
return choice == JOptionPane.YES_OPTION;
}
@FunctionalInterface
private interface GenerateJob {
void run(WallpaperBuilder.ProgressListener listener) throws WallpaperBuilder.BuildException;
}
private void runGenerateJob(File output, GenerateJob job) {
setBusy(true);
logArea.setText("");
setWallpaperButton.setEnabled(false);
SwingWorker<Void, String> worker = new SwingWorker<>() {
@Override
protected Void doInBackground() throws Exception {
job.run(line -> publish(line));
return null;
}
@Override
protected void process(List<String> chunks) {
for (String line : chunks) {
logArea.append(line);
logArea.append("\n");
}
}
@Override
protected void done() {
setBusy(false);
try {
get();
logArea.append("Done. Saved to " + output.getAbsolutePath() + "\n");
lastGeneratedFile = output;
setWallpaperButton.setEnabled(true);
} catch (Exception e) {
Throwable cause = e.getCause() != null ? e.getCause() : e;
logArea.append("Failed: " + cause.getMessage() + "\n");
JOptionPane.showMessageDialog(MainWindow.this, cause.getMessage(),
"Could not generate wallpaper", JOptionPane.ERROR_MESSAGE);
}
}
};
worker.execute();
}
private void setBusy(boolean busy) {
generateButton.setEnabled(!busy);
progressBar.setIndeterminate(busy);
}
private void setAsDesktopPicture() {
if (lastGeneratedFile == null) {
return;
}
SwingWorker<Void, Void> worker = new SwingWorker<>() {
@Override
protected Void doInBackground() throws Exception {
WallpaperInstaller.setAsDesktopPicture(lastGeneratedFile);
return null;
}
@Override
protected void done() {
try {
get();
logArea.append("Set as desktop picture.\n");
} catch (Exception e) {
Throwable cause = e.getCause() != null ? e.getCause() : e;
JOptionPane.showMessageDialog(MainWindow.this, cause.getMessage(),
"Could not set desktop picture", JOptionPane.ERROR_MESSAGE);
}
}
};
worker.execute();
}
}