277 lines
11 KiB
Java
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();
|
|
}
|
|
|
|
}
|