mirror of
https://github.com/packwiz/packwiz-installer.git
synced 2025-04-20 05:26:30 +02:00
Port UI to Kotlin
This commit is contained in:
parent
0770029dc6
commit
bead683b7c
@ -77,10 +77,12 @@ if (project.hasProperty("github.token")) {
|
|||||||
tasks.compileKotlin {
|
tasks.compileKotlin {
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = "1.8"
|
jvmTarget = "1.8"
|
||||||
|
freeCompilerArgs = listOf("-Xjvm-default=enable")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tasks.compileTestKotlin {
|
tasks.compileTestKotlin {
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = "1.8"
|
jvmTarget = "1.8"
|
||||||
|
freeCompilerArgs = listOf("-Xjvm-default=enable")
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,55 +0,0 @@
|
|||||||
package link.infra.packwiz.installer.ui;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
|
|
||||||
public class CLIHandler implements IUserInterface {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleException(Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void show(InputStateHandler h) {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void submitProgress(InstallProgress progress) {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
if (progress.hasProgress) {
|
|
||||||
sb.append('(');
|
|
||||||
sb.append(progress.progress);
|
|
||||||
sb.append('/');
|
|
||||||
sb.append(progress.progressTotal);
|
|
||||||
sb.append(") ");
|
|
||||||
}
|
|
||||||
sb.append(progress.message);
|
|
||||||
System.out.println(sb.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void executeManager(Runnable task) {
|
|
||||||
task.run();
|
|
||||||
System.out.println("Finished successfully!");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Future<Boolean> showOptions(List<IOptionDetails> options) {
|
|
||||||
for (IOptionDetails opt : options) {
|
|
||||||
opt.setOptionValue(true);
|
|
||||||
System.out.println("Warning: accepting option " + opt.getName() + " as option choosing is not implemented in the CLI");
|
|
||||||
}
|
|
||||||
CompletableFuture<Boolean> future = new CompletableFuture<>();
|
|
||||||
future.complete(false); // Can't be cancelled!
|
|
||||||
return future;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Future<IExceptionDetails.ExceptionListResult> showExceptions(List<IExceptionDetails> opts, int numTotal, boolean allowsIgnore) {
|
|
||||||
CompletableFuture<IExceptionDetails.ExceptionListResult> future = new CompletableFuture<>();
|
|
||||||
future.complete(IExceptionDetails.ExceptionListResult.CANCEL);
|
|
||||||
return future;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,183 +0,0 @@
|
|||||||
package link.infra.packwiz.installer.ui;
|
|
||||||
|
|
||||||
import link.infra.packwiz.installer.ui.IExceptionDetails.ExceptionListResult;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import javax.swing.border.EmptyBorder;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.event.WindowAdapter;
|
|
||||||
import java.awt.event.WindowEvent;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.PrintWriter;
|
|
||||||
import java.io.StringWriter;
|
|
||||||
import java.net.URI;
|
|
||||||
import java.net.URISyntaxException;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
|
|
||||||
class ExceptionListWindow extends JDialog {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
private final JTextArea lblExceptionStacktrace;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create the dialog.
|
|
||||||
*/
|
|
||||||
ExceptionListWindow(List<IExceptionDetails> eList, CompletableFuture<ExceptionListResult> future, int numTotal, boolean allowsIgnore, JFrame parentWindow) {
|
|
||||||
super(parentWindow, "Failed file downloads", true);
|
|
||||||
|
|
||||||
setBounds(100, 100, 540, 340);
|
|
||||||
setLocationRelativeTo(parentWindow);
|
|
||||||
getContentPane().setLayout(new BorderLayout());
|
|
||||||
{
|
|
||||||
JPanel errorPanel = new JPanel();
|
|
||||||
getContentPane().add(errorPanel, BorderLayout.NORTH);
|
|
||||||
{
|
|
||||||
JLabel lblWarning = new JLabel("One or more errors were encountered while installing the modpack!");
|
|
||||||
lblWarning.setIcon(UIManager.getIcon("OptionPane.warningIcon"));
|
|
||||||
errorPanel.add(lblWarning);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
JPanel contentPanel = new JPanel();
|
|
||||||
contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
|
|
||||||
getContentPane().add(contentPanel, BorderLayout.CENTER);
|
|
||||||
contentPanel.setLayout(new BorderLayout(0, 0));
|
|
||||||
{
|
|
||||||
JSplitPane splitPane = new JSplitPane();
|
|
||||||
splitPane.setResizeWeight(0.3);
|
|
||||||
contentPanel.add(splitPane);
|
|
||||||
{
|
|
||||||
lblExceptionStacktrace = new JTextArea("Select a file");
|
|
||||||
lblExceptionStacktrace.setBackground(UIManager.getColor("List.background"));
|
|
||||||
lblExceptionStacktrace.setOpaque(true);
|
|
||||||
lblExceptionStacktrace.setWrapStyleWord(true);
|
|
||||||
lblExceptionStacktrace.setLineWrap(true);
|
|
||||||
lblExceptionStacktrace.setEditable(false);
|
|
||||||
lblExceptionStacktrace.setFocusable(true);
|
|
||||||
lblExceptionStacktrace.setFont(UIManager.getFont("Label.font"));
|
|
||||||
lblExceptionStacktrace.setBorder(new EmptyBorder(5, 5, 5, 5));
|
|
||||||
JScrollPane scrollPane = new JScrollPane(lblExceptionStacktrace);
|
|
||||||
scrollPane.setBorder(new EmptyBorder(0, 0, 0, 0));
|
|
||||||
splitPane.setRightComponent(scrollPane);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
JList<String> list = new JList<>();
|
|
||||||
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
|
||||||
list.setBorder(new EmptyBorder(5, 5, 5, 5));
|
|
||||||
ExceptionListModel listModel = new ExceptionListModel(eList);
|
|
||||||
list.setModel(listModel);
|
|
||||||
list.addListSelectionListener(e -> {
|
|
||||||
int i = list.getSelectedIndex();
|
|
||||||
if (i > -1) {
|
|
||||||
StringWriter sw = new StringWriter();
|
|
||||||
listModel.getExceptionAt(i).printStackTrace(new PrintWriter(sw));
|
|
||||||
lblExceptionStacktrace.setText(sw.toString());
|
|
||||||
// Scroll to the top
|
|
||||||
lblExceptionStacktrace.setCaretPosition(0);
|
|
||||||
} else {
|
|
||||||
lblExceptionStacktrace.setText("Select a file");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
JScrollPane scrollPane = new JScrollPane(list);
|
|
||||||
scrollPane.setBorder(new EmptyBorder(0, 0, 0, 0));
|
|
||||||
splitPane.setLeftComponent(scrollPane);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
JPanel buttonPane = new JPanel();
|
|
||||||
getContentPane().add(buttonPane, BorderLayout.SOUTH);
|
|
||||||
buttonPane.setLayout(new BorderLayout(0, 0));
|
|
||||||
{
|
|
||||||
JPanel rightButtons = new JPanel();
|
|
||||||
buttonPane.add(rightButtons, BorderLayout.EAST);
|
|
||||||
{
|
|
||||||
JButton btnContinue = new JButton("Continue");
|
|
||||||
btnContinue.setToolTipText("Attempt to continue installing, excluding the failed downloads");
|
|
||||||
btnContinue.addActionListener(e -> {
|
|
||||||
future.complete(ExceptionListResult.CONTINUE);
|
|
||||||
ExceptionListWindow.this.dispose();
|
|
||||||
});
|
|
||||||
rightButtons.add(btnContinue);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
JButton btnCancelLaunch = new JButton("Cancel launch");
|
|
||||||
btnCancelLaunch.setToolTipText("Stop launching the game");
|
|
||||||
btnCancelLaunch.addActionListener(e -> {
|
|
||||||
future.complete(ExceptionListResult.CANCEL);
|
|
||||||
ExceptionListWindow.this.dispose();
|
|
||||||
});
|
|
||||||
rightButtons.add(btnCancelLaunch);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
JButton btnIgnoreUpdate = new JButton("Ignore update");
|
|
||||||
btnIgnoreUpdate.setEnabled(allowsIgnore);
|
|
||||||
btnIgnoreUpdate.setToolTipText("Start the game without attempting to update");
|
|
||||||
btnIgnoreUpdate.addActionListener(e -> {
|
|
||||||
future.complete(ExceptionListResult.IGNORE);
|
|
||||||
ExceptionListWindow.this.dispose();
|
|
||||||
});
|
|
||||||
rightButtons.add(btnIgnoreUpdate);
|
|
||||||
{
|
|
||||||
JLabel lblErrored = new JLabel(eList.size() + "/" + numTotal + " errored");
|
|
||||||
lblErrored.setHorizontalAlignment(SwingConstants.CENTER);
|
|
||||||
buttonPane.add(lblErrored, BorderLayout.CENTER);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
JPanel leftButtons = new JPanel();
|
|
||||||
buttonPane.add(leftButtons, BorderLayout.WEST);
|
|
||||||
{
|
|
||||||
JButton btnReportIssue = new JButton("Report issue");
|
|
||||||
boolean supported = Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE);
|
|
||||||
btnReportIssue.setEnabled(supported);
|
|
||||||
if (supported) {
|
|
||||||
btnReportIssue.addActionListener(e -> {
|
|
||||||
try {
|
|
||||||
Desktop.getDesktop().browse(new URI("https://github.com/comp500/packwiz-installer/issues/new"));
|
|
||||||
} catch (IOException | URISyntaxException e1) {
|
|
||||||
// lol the button just won't work i guess
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
leftButtons.add(btnReportIssue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
addWindowListener(new WindowAdapter() {
|
|
||||||
@Override
|
|
||||||
public void windowClosing(WindowEvent e) {
|
|
||||||
future.complete(ExceptionListResult.CANCEL);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void windowClosed(WindowEvent e) {
|
|
||||||
// Just in case closing didn't get triggered - if something else called dispose() the
|
|
||||||
// future will have already completed
|
|
||||||
future.complete(ExceptionListResult.CANCEL);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ExceptionListModel extends AbstractListModel<String> {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
private final List<IExceptionDetails> details;
|
|
||||||
|
|
||||||
ExceptionListModel(List<IExceptionDetails> details) {
|
|
||||||
this.details = details;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getSize() {
|
|
||||||
return details.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getElementAt(int index) {
|
|
||||||
return details.get(index).getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
Exception getExceptionAt(int index) {
|
|
||||||
return details.get(index).getException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
package link.infra.packwiz.installer.ui;
|
|
||||||
|
|
||||||
public interface IExceptionDetails {
|
|
||||||
Exception getException();
|
|
||||||
String getName();
|
|
||||||
|
|
||||||
enum ExceptionListResult {
|
|
||||||
CONTINUE, CANCEL, IGNORE
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
package link.infra.packwiz.installer.ui;
|
|
||||||
|
|
||||||
public interface IOptionDetails {
|
|
||||||
String getName();
|
|
||||||
boolean getOptionValue();
|
|
||||||
String getOptionDescription();
|
|
||||||
void setOptionValue(boolean value);
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
package link.infra.packwiz.installer.ui;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
|
|
||||||
public interface IUserInterface {
|
|
||||||
|
|
||||||
void show(InputStateHandler handler);
|
|
||||||
|
|
||||||
void handleException(Exception e);
|
|
||||||
|
|
||||||
default void handleExceptionAndExit(Exception e) {
|
|
||||||
handleException(e);
|
|
||||||
System.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
default void setTitle(String title) {}
|
|
||||||
|
|
||||||
void submitProgress(InstallProgress progress);
|
|
||||||
|
|
||||||
void executeManager(Runnable task);
|
|
||||||
|
|
||||||
// Return true if the installation was cancelled!
|
|
||||||
Future<Boolean> showOptions(List<IOptionDetails> option);
|
|
||||||
|
|
||||||
Future<IExceptionDetails.ExceptionListResult> showExceptions(List<IExceptionDetails> opts, int numTotal, boolean allowsIgnore);
|
|
||||||
|
|
||||||
default void disableOptionsButton() {}
|
|
||||||
|
|
||||||
// Should not return CONTINUE
|
|
||||||
default Future<IExceptionDetails.ExceptionListResult> showCancellationDialog() {
|
|
||||||
CompletableFuture<IExceptionDetails.ExceptionListResult> future = new CompletableFuture<>();
|
|
||||||
future.complete(IExceptionDetails.ExceptionListResult.CANCEL);
|
|
||||||
return future;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
package link.infra.packwiz.installer.ui;
|
|
||||||
|
|
||||||
public class InputStateHandler {
|
|
||||||
private boolean optionsButtonPressed = false;
|
|
||||||
private boolean cancelButtonPressed = false;
|
|
||||||
|
|
||||||
synchronized void pressCancelButton() {
|
|
||||||
this.cancelButtonPressed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized void pressOptionsButton() {
|
|
||||||
this.optionsButtonPressed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized boolean getCancelButton() {
|
|
||||||
return cancelButtonPressed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized boolean getOptionsButton() {
|
|
||||||
return optionsButtonPressed;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
package link.infra.packwiz.installer.ui;
|
|
||||||
|
|
||||||
public class InstallProgress {
|
|
||||||
public final String message;
|
|
||||||
public final boolean hasProgress;
|
|
||||||
public final int progress;
|
|
||||||
public final int progressTotal;
|
|
||||||
|
|
||||||
public InstallProgress(String message) {
|
|
||||||
this.message = message;
|
|
||||||
hasProgress = false;
|
|
||||||
progress = 0;
|
|
||||||
progressTotal = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public InstallProgress(String message, int progress, int progressTotal) {
|
|
||||||
this.message = message;
|
|
||||||
hasProgress = true;
|
|
||||||
this.progress = progress;
|
|
||||||
this.progressTotal = progressTotal;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,228 +0,0 @@
|
|||||||
package link.infra.packwiz.installer.ui;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import javax.swing.border.EmptyBorder;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
|
|
||||||
public class InstallWindow implements IUserInterface {
|
|
||||||
|
|
||||||
private JFrame frmPackwizlauncher;
|
|
||||||
private JLabel lblProgresslabel;
|
|
||||||
private JProgressBar progressBar;
|
|
||||||
private InputStateHandler inputStateHandler;
|
|
||||||
|
|
||||||
private String title = "Updating modpack...";
|
|
||||||
private SwingWorkerButWithPublicPublish<Void, InstallProgress> worker;
|
|
||||||
private AtomicBoolean aboutToCrash = new AtomicBoolean();
|
|
||||||
private JButton btnOptions;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void show(InputStateHandler handler) {
|
|
||||||
this.inputStateHandler = handler;
|
|
||||||
EventQueue.invokeLater(() -> {
|
|
||||||
try {
|
|
||||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
|
||||||
initialize();
|
|
||||||
frmPackwizlauncher.setVisible(true);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the contents of the frame.
|
|
||||||
* @wbp.parser.entryPoint
|
|
||||||
*/
|
|
||||||
private void initialize() {
|
|
||||||
frmPackwizlauncher = new JFrame();
|
|
||||||
frmPackwizlauncher.setTitle(title);
|
|
||||||
frmPackwizlauncher.setBounds(100, 100, 493, 95);
|
|
||||||
frmPackwizlauncher.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
|
||||||
frmPackwizlauncher.setLocationRelativeTo(null);
|
|
||||||
|
|
||||||
JPanel panel = new JPanel();
|
|
||||||
panel.setBorder(new EmptyBorder(10, 10, 10, 10));
|
|
||||||
frmPackwizlauncher.getContentPane().add(panel, BorderLayout.CENTER);
|
|
||||||
panel.setLayout(new BorderLayout(0, 0));
|
|
||||||
|
|
||||||
progressBar = new JProgressBar();
|
|
||||||
progressBar.setIndeterminate(true);
|
|
||||||
panel.add(progressBar, BorderLayout.CENTER);
|
|
||||||
|
|
||||||
lblProgresslabel = new JLabel("Loading...");
|
|
||||||
panel.add(lblProgresslabel, BorderLayout.SOUTH);
|
|
||||||
|
|
||||||
JPanel panel_1 = new JPanel();
|
|
||||||
panel_1.setBorder(new EmptyBorder(0, 5, 0, 5));
|
|
||||||
frmPackwizlauncher.getContentPane().add(panel_1, BorderLayout.EAST);
|
|
||||||
GridBagLayout gbl_panel_1 = new GridBagLayout();
|
|
||||||
panel_1.setLayout(gbl_panel_1);
|
|
||||||
|
|
||||||
btnOptions = new JButton("Optional mods...");
|
|
||||||
btnOptions.addActionListener(e -> {
|
|
||||||
btnOptions.setText("Loading...");
|
|
||||||
btnOptions.setEnabled(false);
|
|
||||||
inputStateHandler.pressOptionsButton();
|
|
||||||
});
|
|
||||||
btnOptions.setAlignmentX(Component.CENTER_ALIGNMENT);
|
|
||||||
GridBagConstraints gbc_btnOptions = new GridBagConstraints();
|
|
||||||
gbc_btnOptions.gridx = 0;
|
|
||||||
gbc_btnOptions.gridy = 0;
|
|
||||||
panel_1.add(btnOptions, gbc_btnOptions);
|
|
||||||
|
|
||||||
JButton btnCancel = new JButton("Cancel");
|
|
||||||
btnCancel.addActionListener(e -> {
|
|
||||||
btnCancel.setEnabled(false);
|
|
||||||
inputStateHandler.pressCancelButton();
|
|
||||||
});
|
|
||||||
btnCancel.setAlignmentX(Component.CENTER_ALIGNMENT);
|
|
||||||
GridBagConstraints gbc_btnCancel = new GridBagConstraints();
|
|
||||||
gbc_btnCancel.gridx = 0;
|
|
||||||
gbc_btnCancel.gridy = 1;
|
|
||||||
panel_1.add(btnCancel, gbc_btnCancel);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleException(Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
EventQueue.invokeLater(() -> {
|
|
||||||
JOptionPane.showMessageDialog(null, "An error occurred: \n" + e.getClass().getCanonicalName() + ": " + e.getMessage(), title, JOptionPane.ERROR_MESSAGE);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleExceptionAndExit(Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
// Used to prevent the done() handler of SwingWorker executing if the invokeLater hasn't happened yet
|
|
||||||
aboutToCrash.set(true);
|
|
||||||
EventQueue.invokeLater(() -> {
|
|
||||||
JOptionPane.showMessageDialog(null, "A fatal error occurred: \n" + e.getClass().getCanonicalName() + ": " + e.getMessage(), title, JOptionPane.ERROR_MESSAGE);
|
|
||||||
System.exit(1);
|
|
||||||
});
|
|
||||||
// Pause forever, so it blocks while we wait for System.exit to take effect
|
|
||||||
try {
|
|
||||||
Thread.currentThread().join();
|
|
||||||
} catch (InterruptedException ex) {
|
|
||||||
// no u
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setTitle(String title) {
|
|
||||||
this.title = title;
|
|
||||||
if (frmPackwizlauncher != null) {
|
|
||||||
EventQueue.invokeLater(() -> frmPackwizlauncher.setTitle(title));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void submitProgress(InstallProgress progress) {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
if (progress.hasProgress) {
|
|
||||||
sb.append('(');
|
|
||||||
sb.append(progress.progress);
|
|
||||||
sb.append('/');
|
|
||||||
sb.append(progress.progressTotal);
|
|
||||||
sb.append(") ");
|
|
||||||
}
|
|
||||||
sb.append(progress.message);
|
|
||||||
// TODO: better logging library?
|
|
||||||
System.out.println(sb.toString());
|
|
||||||
if (worker != null) {
|
|
||||||
worker.publishPublic(progress);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void executeManager(Runnable task) {
|
|
||||||
EventQueue.invokeLater(() -> {
|
|
||||||
worker = new SwingWorkerButWithPublicPublish<Void, InstallProgress>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Void doInBackground() {
|
|
||||||
task.run();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void process(List<InstallProgress> chunks) {
|
|
||||||
// Only process last chunk
|
|
||||||
if (chunks.size() > 0) {
|
|
||||||
InstallProgress prog = chunks.get(chunks.size() - 1);
|
|
||||||
if (prog.hasProgress) {
|
|
||||||
progressBar.setIndeterminate(false);
|
|
||||||
progressBar.setValue(prog.progress);
|
|
||||||
progressBar.setMaximum(prog.progressTotal);
|
|
||||||
} else {
|
|
||||||
progressBar.setIndeterminate(true);
|
|
||||||
progressBar.setValue(0);
|
|
||||||
}
|
|
||||||
lblProgresslabel.setText(prog.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void done() {
|
|
||||||
if (aboutToCrash.get()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// TODO: a better way to do this?
|
|
||||||
frmPackwizlauncher.dispose();
|
|
||||||
System.out.println("Finished successfully!");
|
|
||||||
System.exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
worker.execute();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Future<Boolean> showOptions(List<IOptionDetails> opts) {
|
|
||||||
CompletableFuture<Boolean> future = new CompletableFuture<>();
|
|
||||||
EventQueue.invokeLater(() -> {
|
|
||||||
OptionsSelectWindow dialog = new OptionsSelectWindow(opts, future, frmPackwizlauncher);
|
|
||||||
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
|
|
||||||
dialog.setVisible(true);
|
|
||||||
});
|
|
||||||
return future;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Future<IExceptionDetails.ExceptionListResult> showExceptions(List<IExceptionDetails> opts, int numTotal, boolean allowsIgnore) {
|
|
||||||
CompletableFuture<IExceptionDetails.ExceptionListResult> future = new CompletableFuture<>();
|
|
||||||
EventQueue.invokeLater(() -> {
|
|
||||||
ExceptionListWindow dialog = new ExceptionListWindow(opts, future, numTotal, allowsIgnore, frmPackwizlauncher);
|
|
||||||
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
|
|
||||||
dialog.setVisible(true);
|
|
||||||
});
|
|
||||||
return future;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void disableOptionsButton() {
|
|
||||||
if (btnOptions != null) {
|
|
||||||
btnOptions.setText("Optional mods...");
|
|
||||||
btnOptions.setEnabled(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Future<IExceptionDetails.ExceptionListResult> showCancellationDialog() {
|
|
||||||
CompletableFuture<IExceptionDetails.ExceptionListResult> future = new CompletableFuture<>();
|
|
||||||
EventQueue.invokeLater(() -> {
|
|
||||||
Object[] buttons = {"Quit", "Ignore"};
|
|
||||||
int result = JOptionPane.showOptionDialog(frmPackwizlauncher,
|
|
||||||
"The installation was cancelled. Would you like to quit the game, or ignore the update and start the game?",
|
|
||||||
"Cancelled installation",
|
|
||||||
JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, buttons, buttons[0]);
|
|
||||||
future.complete(result == 0 ? IExceptionDetails.ExceptionListResult.CANCEL : IExceptionDetails.ExceptionListResult.IGNORE);
|
|
||||||
});
|
|
||||||
return future;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
package link.infra.packwiz.installer.ui;
|
|
||||||
|
|
||||||
// Serves as a proxy for IOptionDetails, so that setOptionValue isn't called until OK is clicked
|
|
||||||
class OptionTempHandler implements IOptionDetails {
|
|
||||||
private final IOptionDetails opt;
|
|
||||||
private boolean tempValue;
|
|
||||||
|
|
||||||
OptionTempHandler(IOptionDetails opt) {
|
|
||||||
this.opt = opt;
|
|
||||||
tempValue = opt.getOptionValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return opt.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getOptionDescription() {
|
|
||||||
return opt.getOptionDescription();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean getOptionValue() {
|
|
||||||
return tempValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOptionValue(boolean value) {
|
|
||||||
tempValue = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void finalise() {
|
|
||||||
opt.setOptionValue(tempValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,205 +0,0 @@
|
|||||||
package link.infra.packwiz.installer.ui;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import javax.swing.border.EmptyBorder;
|
|
||||||
import javax.swing.event.ListSelectionEvent;
|
|
||||||
import javax.swing.event.ListSelectionListener;
|
|
||||||
import javax.swing.event.TableModelListener;
|
|
||||||
import javax.swing.table.TableModel;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import java.awt.event.WindowAdapter;
|
|
||||||
import java.awt.event.WindowEvent;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
|
|
||||||
public class OptionsSelectWindow extends JDialog implements ActionListener {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
private final JTextArea lblOptionDescription;
|
|
||||||
private final OptionTableModel tableModel;
|
|
||||||
private final CompletableFuture<Boolean> future;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create the dialog.
|
|
||||||
*/
|
|
||||||
OptionsSelectWindow(List<IOptionDetails> optList, CompletableFuture<Boolean> future, JFrame parentWindow) {
|
|
||||||
super(parentWindow, "Select optional mods...", true);
|
|
||||||
|
|
||||||
tableModel = new OptionTableModel(optList);
|
|
||||||
this.future = future;
|
|
||||||
|
|
||||||
setBounds(100, 100, 450, 300);
|
|
||||||
setLocationRelativeTo(parentWindow);
|
|
||||||
getContentPane().setLayout(new BorderLayout());
|
|
||||||
JPanel contentPanel = new JPanel();
|
|
||||||
contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
|
|
||||||
getContentPane().add(contentPanel, BorderLayout.CENTER);
|
|
||||||
contentPanel.setLayout(new BorderLayout(0, 0));
|
|
||||||
{
|
|
||||||
JSplitPane splitPane = new JSplitPane();
|
|
||||||
splitPane.setResizeWeight(0.5);
|
|
||||||
contentPanel.add(splitPane);
|
|
||||||
{
|
|
||||||
JTable table = new JTable();
|
|
||||||
table.setShowVerticalLines(false);
|
|
||||||
table.setShowHorizontalLines(false);
|
|
||||||
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
|
||||||
table.setShowGrid(false);
|
|
||||||
table.setModel(tableModel);
|
|
||||||
table.getColumnModel().getColumn(0).setResizable(false);
|
|
||||||
table.getColumnModel().getColumn(0).setPreferredWidth(15);
|
|
||||||
table.getColumnModel().getColumn(0).setMaxWidth(15);
|
|
||||||
table.getColumnModel().getColumn(1).setResizable(false);
|
|
||||||
table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
|
|
||||||
@Override
|
|
||||||
public void valueChanged(ListSelectionEvent e) {
|
|
||||||
int i = table.getSelectedRow();
|
|
||||||
if (i > -1) {
|
|
||||||
lblOptionDescription.setText(tableModel.getDescription(i));
|
|
||||||
} else {
|
|
||||||
lblOptionDescription.setText("Select an option...");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
table.setTableHeader(null);
|
|
||||||
JScrollPane scrollPane = new JScrollPane(table);
|
|
||||||
scrollPane.getViewport().setBackground(UIManager.getColor("List.background"));
|
|
||||||
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
|
||||||
scrollPane.setBorder(new EmptyBorder(0, 0, 0, 0));
|
|
||||||
splitPane.setLeftComponent(scrollPane);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
lblOptionDescription = new JTextArea("Select an option...");
|
|
||||||
lblOptionDescription.setBackground(UIManager.getColor("List.background"));
|
|
||||||
lblOptionDescription.setOpaque(true);
|
|
||||||
lblOptionDescription.setWrapStyleWord(true);
|
|
||||||
lblOptionDescription.setLineWrap(true);
|
|
||||||
lblOptionDescription.setEditable(false);
|
|
||||||
lblOptionDescription.setFocusable(false);
|
|
||||||
lblOptionDescription.setFont(UIManager.getFont("Label.font"));
|
|
||||||
lblOptionDescription.setBorder(new EmptyBorder(10, 10, 10, 10));
|
|
||||||
JScrollPane scrollPane = new JScrollPane(lblOptionDescription);
|
|
||||||
scrollPane.setBorder(new EmptyBorder(0, 0, 0, 0));
|
|
||||||
splitPane.setRightComponent(scrollPane);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
JPanel buttonPane = new JPanel();
|
|
||||||
buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT));
|
|
||||||
getContentPane().add(buttonPane, BorderLayout.SOUTH);
|
|
||||||
{
|
|
||||||
JButton okButton = new JButton("OK");
|
|
||||||
okButton.setActionCommand("OK");
|
|
||||||
okButton.addActionListener(this);
|
|
||||||
buttonPane.add(okButton);
|
|
||||||
getRootPane().setDefaultButton(okButton);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
JButton cancelButton = new JButton("Cancel");
|
|
||||||
cancelButton.setActionCommand("Cancel");
|
|
||||||
cancelButton.addActionListener(this);
|
|
||||||
buttonPane.add(cancelButton);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
addWindowListener(new WindowAdapter() {
|
|
||||||
@Override
|
|
||||||
public void windowClosing(WindowEvent e) {
|
|
||||||
future.complete(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void windowClosed(WindowEvent e) {
|
|
||||||
// Just in case closing didn't get triggered - if something else called dispose() the
|
|
||||||
// future will have already completed
|
|
||||||
future.complete(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class OptionTableModel implements TableModel {
|
|
||||||
private List<OptionTempHandler> opts = new ArrayList<>();
|
|
||||||
|
|
||||||
OptionTableModel(List<IOptionDetails> givenOpts) {
|
|
||||||
for (IOptionDetails opt : givenOpts) {
|
|
||||||
opts.add(new OptionTempHandler(opt));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getRowCount() {
|
|
||||||
return opts.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getColumnCount() {
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final String[] columnNames = {"Enabled", "Mod name"};
|
|
||||||
private final Class<?>[] columnTypes = {Boolean.class, String.class};
|
|
||||||
private final boolean[] columnEditables = {true, false};
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getColumnName(int columnIndex) {
|
|
||||||
return columnNames[columnIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<?> getColumnClass(int columnIndex) {
|
|
||||||
return columnTypes[columnIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCellEditable(int rowIndex, int columnIndex) {
|
|
||||||
return columnEditables[columnIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getValueAt(int rowIndex, int columnIndex) {
|
|
||||||
OptionTempHandler opt = opts.get(rowIndex);
|
|
||||||
return columnIndex == 0 ? opt.getOptionValue() : opt.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
|
|
||||||
if (columnIndex == 0) {
|
|
||||||
OptionTempHandler opt = opts.get(rowIndex);
|
|
||||||
opt.setOptionValue((boolean) aValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Noop, the table model doesn't change!
|
|
||||||
@Override
|
|
||||||
public void addTableModelListener(TableModelListener l) {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeTableModelListener(TableModelListener l) {}
|
|
||||||
|
|
||||||
String getDescription(int rowIndex) {
|
|
||||||
return opts.get(rowIndex).getOptionDescription();
|
|
||||||
}
|
|
||||||
|
|
||||||
void finalise() {
|
|
||||||
for (OptionTempHandler opt : opts) {
|
|
||||||
opt.finalise();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
if (e.getActionCommand().equals("OK")) {
|
|
||||||
tableModel.finalise();
|
|
||||||
future.complete(false);
|
|
||||||
dispose();
|
|
||||||
} else if (e.getActionCommand().equals("Cancel")) {
|
|
||||||
future.complete(true);
|
|
||||||
dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
package link.infra.packwiz.installer.ui;
|
|
||||||
|
|
||||||
import javax.swing.SwingWorker;
|
|
||||||
|
|
||||||
// Q: AAA WHAT HAVE YOU DONE THIS IS DISGUSTING
|
|
||||||
// A: it just makes things easier, so i can easily have one interface for CLI/GUI
|
|
||||||
// if someone has a better way to do this please PR it
|
|
||||||
public abstract class SwingWorkerButWithPublicPublish<T,V> extends SwingWorker<T,V> {
|
|
||||||
@SafeVarargs
|
|
||||||
public final void publishPublic(V... chunks) {
|
|
||||||
publish(chunks);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,47 @@
|
|||||||
|
package link.infra.packwiz.installer.ui
|
||||||
|
|
||||||
|
import link.infra.packwiz.installer.ui.IExceptionDetails.ExceptionListResult
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
import java.util.concurrent.Future
|
||||||
|
|
||||||
|
class CLIHandler : IUserInterface {
|
||||||
|
override fun handleException(e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun show(handler: InputStateHandler) {}
|
||||||
|
override fun submitProgress(progress: InstallProgress) {
|
||||||
|
val sb = StringBuilder()
|
||||||
|
if (progress.hasProgress) {
|
||||||
|
sb.append('(')
|
||||||
|
sb.append(progress.progress)
|
||||||
|
sb.append('/')
|
||||||
|
sb.append(progress.progressTotal)
|
||||||
|
sb.append(") ")
|
||||||
|
}
|
||||||
|
sb.append(progress.message)
|
||||||
|
println(sb.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun executeManager(task: () -> Unit) {
|
||||||
|
task()
|
||||||
|
println("Finished successfully!")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showOptions(options: List<IOptionDetails>): Future<Boolean> {
|
||||||
|
for (opt in options) {
|
||||||
|
opt.optionValue = true
|
||||||
|
// TODO: implement option choice in the CLI?
|
||||||
|
println("Warning: accepting option " + opt.name + " as option choosing is not implemented in the CLI")
|
||||||
|
}
|
||||||
|
return CompletableFuture<Boolean>().apply {
|
||||||
|
complete(false) // Can't be cancelled!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showExceptions(exceptions: List<IExceptionDetails>, numTotal: Int, allowsIgnore: Boolean): Future<ExceptionListResult> {
|
||||||
|
val future = CompletableFuture<ExceptionListResult>()
|
||||||
|
future.complete(ExceptionListResult.CANCEL)
|
||||||
|
return future
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,152 @@
|
|||||||
|
package link.infra.packwiz.installer.ui
|
||||||
|
|
||||||
|
import link.infra.packwiz.installer.ui.IExceptionDetails.ExceptionListResult
|
||||||
|
import java.awt.BorderLayout
|
||||||
|
import java.awt.Desktop
|
||||||
|
import java.awt.event.WindowAdapter
|
||||||
|
import java.awt.event.WindowEvent
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.PrintWriter
|
||||||
|
import java.io.StringWriter
|
||||||
|
import java.net.URI
|
||||||
|
import java.net.URISyntaxException
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
import javax.swing.*
|
||||||
|
import javax.swing.border.EmptyBorder
|
||||||
|
|
||||||
|
internal class ExceptionListWindow(eList: List<IExceptionDetails>, future: CompletableFuture<ExceptionListResult>, numTotal: Int, allowsIgnore: Boolean, parentWindow: JFrame?) : JDialog(parentWindow, "Failed file downloads", true) {
|
||||||
|
private val lblExceptionStacktrace: JTextArea
|
||||||
|
|
||||||
|
private class ExceptionListModel internal constructor(private val details: List<IExceptionDetails>) : AbstractListModel<String>() {
|
||||||
|
override fun getSize() = details.size
|
||||||
|
override fun getElementAt(index: Int) = details[index].name
|
||||||
|
fun getExceptionAt(index: Int) = details[index].exception
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the dialog.
|
||||||
|
*/
|
||||||
|
init {
|
||||||
|
setBounds(100, 100, 540, 340)
|
||||||
|
setLocationRelativeTo(parentWindow)
|
||||||
|
|
||||||
|
contentPane.apply {
|
||||||
|
layout = BorderLayout()
|
||||||
|
|
||||||
|
// Error panel
|
||||||
|
add(JPanel().apply {
|
||||||
|
add(JLabel("One or more errors were encountered while installing the modpack!").apply {
|
||||||
|
icon = UIManager.getIcon("OptionPane.warningIcon")
|
||||||
|
})
|
||||||
|
}, BorderLayout.NORTH)
|
||||||
|
|
||||||
|
// Content panel
|
||||||
|
add(JPanel().apply {
|
||||||
|
border = EmptyBorder(5, 5, 5, 5)
|
||||||
|
layout = BorderLayout(0, 0)
|
||||||
|
|
||||||
|
add(JSplitPane().apply {
|
||||||
|
resizeWeight = 0.3
|
||||||
|
|
||||||
|
lblExceptionStacktrace = JTextArea("Select a file")
|
||||||
|
lblExceptionStacktrace.background = UIManager.getColor("List.background")
|
||||||
|
lblExceptionStacktrace.isOpaque = true
|
||||||
|
lblExceptionStacktrace.wrapStyleWord = true
|
||||||
|
lblExceptionStacktrace.lineWrap = true
|
||||||
|
lblExceptionStacktrace.isEditable = false
|
||||||
|
lblExceptionStacktrace.isFocusable = true
|
||||||
|
lblExceptionStacktrace.font = UIManager.getFont("Label.font")
|
||||||
|
lblExceptionStacktrace.border = EmptyBorder(5, 5, 5, 5)
|
||||||
|
|
||||||
|
rightComponent = JScrollPane(lblExceptionStacktrace)
|
||||||
|
|
||||||
|
leftComponent = JScrollPane(JList<String>().apply {
|
||||||
|
selectionMode = ListSelectionModel.SINGLE_SELECTION
|
||||||
|
border = EmptyBorder(5, 5, 5, 5)
|
||||||
|
val listModel = ExceptionListModel(eList)
|
||||||
|
model = listModel
|
||||||
|
addListSelectionListener {
|
||||||
|
val i = selectedIndex
|
||||||
|
if (i > -1) {
|
||||||
|
val sw = StringWriter()
|
||||||
|
listModel.getExceptionAt(i).printStackTrace(PrintWriter(sw))
|
||||||
|
lblExceptionStacktrace.text = sw.toString()
|
||||||
|
// Scroll to the top
|
||||||
|
lblExceptionStacktrace.caretPosition = 0
|
||||||
|
} else {
|
||||||
|
lblExceptionStacktrace.text = "Select a file"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}, BorderLayout.CENTER)
|
||||||
|
|
||||||
|
// Button pane
|
||||||
|
add(JPanel().apply {
|
||||||
|
layout = BorderLayout(0, 0)
|
||||||
|
|
||||||
|
// Right buttons
|
||||||
|
add(JPanel().apply {
|
||||||
|
add(JButton("Continue").apply {
|
||||||
|
toolTipText = "Attempt to continue installing, excluding the failed downloads"
|
||||||
|
addActionListener {
|
||||||
|
future.complete(ExceptionListResult.CONTINUE)
|
||||||
|
this@ExceptionListWindow.dispose()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
add(JButton("Cancel launch").apply {
|
||||||
|
toolTipText = "Stop launching the game"
|
||||||
|
addActionListener {
|
||||||
|
future.complete(ExceptionListResult.CANCEL)
|
||||||
|
this@ExceptionListWindow.dispose()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
add(JButton("Ignore update").apply {
|
||||||
|
toolTipText = "Start the game without attempting to update"
|
||||||
|
isEnabled = allowsIgnore
|
||||||
|
addActionListener {
|
||||||
|
future.complete(ExceptionListResult.IGNORE)
|
||||||
|
this@ExceptionListWindow.dispose()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, BorderLayout.EAST)
|
||||||
|
|
||||||
|
// Errored label
|
||||||
|
add(JLabel(eList.size.toString() + "/" + numTotal + " errored").apply {
|
||||||
|
horizontalAlignment = SwingConstants.CENTER
|
||||||
|
}, BorderLayout.CENTER)
|
||||||
|
|
||||||
|
// Left buttons
|
||||||
|
add(JPanel().apply {
|
||||||
|
add(JButton("Report issue").apply {
|
||||||
|
if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
|
||||||
|
addActionListener {
|
||||||
|
try {
|
||||||
|
Desktop.getDesktop().browse(URI("https://github.com/comp500/packwiz-installer/issues/new"))
|
||||||
|
} catch (e: IOException) {
|
||||||
|
// lol the button just won't work i guess
|
||||||
|
} catch (e: URISyntaxException) {}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
isEnabled = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, BorderLayout.WEST)
|
||||||
|
}, BorderLayout.SOUTH)
|
||||||
|
}
|
||||||
|
|
||||||
|
addWindowListener(object : WindowAdapter() {
|
||||||
|
override fun windowClosing(e: WindowEvent) {
|
||||||
|
future.complete(ExceptionListResult.CANCEL)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun windowClosed(e: WindowEvent) {
|
||||||
|
// Just in case closing didn't get triggered - if something else called dispose() the
|
||||||
|
// future will have already completed
|
||||||
|
future.complete(ExceptionListResult.CANCEL)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package link.infra.packwiz.installer.ui
|
||||||
|
|
||||||
|
interface IExceptionDetails {
|
||||||
|
val exception: Exception
|
||||||
|
val name: String
|
||||||
|
|
||||||
|
enum class ExceptionListResult {
|
||||||
|
CONTINUE, CANCEL, IGNORE
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package link.infra.packwiz.installer.ui
|
||||||
|
|
||||||
|
interface IOptionDetails {
|
||||||
|
val name: String
|
||||||
|
var optionValue: Boolean
|
||||||
|
val optionDescription: String
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package link.infra.packwiz.installer.ui
|
||||||
|
|
||||||
|
import link.infra.packwiz.installer.ui.IExceptionDetails.ExceptionListResult
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
import java.util.concurrent.Future
|
||||||
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
|
interface IUserInterface {
|
||||||
|
fun show(handler: InputStateHandler)
|
||||||
|
fun handleException(e: Exception)
|
||||||
|
@JvmDefault
|
||||||
|
fun handleExceptionAndExit(e: Exception) {
|
||||||
|
handleException(e)
|
||||||
|
exitProcess(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmDefault
|
||||||
|
fun setTitle(title: String) {}
|
||||||
|
fun submitProgress(progress: InstallProgress)
|
||||||
|
fun executeManager(task: () -> Unit)
|
||||||
|
// Return true if the installation was cancelled!
|
||||||
|
fun showOptions(options: List<IOptionDetails>): Future<Boolean>
|
||||||
|
|
||||||
|
fun showExceptions(exceptions: List<IExceptionDetails>, numTotal: Int, allowsIgnore: Boolean): Future<ExceptionListResult>
|
||||||
|
@JvmDefault
|
||||||
|
fun disableOptionsButton() {}
|
||||||
|
// Should not return CONTINUE
|
||||||
|
@JvmDefault
|
||||||
|
fun showCancellationDialog(): Future<ExceptionListResult> {
|
||||||
|
return CompletableFuture<ExceptionListResult>().apply {
|
||||||
|
complete(ExceptionListResult.CANCEL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package link.infra.packwiz.installer.ui
|
||||||
|
|
||||||
|
class InputStateHandler {
|
||||||
|
// TODO: convert to coroutines/locks?
|
||||||
|
@get:Synchronized
|
||||||
|
var optionsButton = false
|
||||||
|
private set
|
||||||
|
@get:Synchronized
|
||||||
|
var cancelButton = false
|
||||||
|
private set
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun pressCancelButton() {
|
||||||
|
cancelButton = true
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun pressOptionsButton() {
|
||||||
|
optionsButton = true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package link.infra.packwiz.installer.ui
|
||||||
|
|
||||||
|
data class InstallProgress(
|
||||||
|
val message: String,
|
||||||
|
val hasProgress: Boolean = false,
|
||||||
|
val progress: Int = 0,
|
||||||
|
val progressTotal: Int = 0
|
||||||
|
) {
|
||||||
|
constructor(message: String, progress: Int, progressTotal: Int) : this(message, true, progress, progressTotal)
|
||||||
|
|
||||||
|
constructor(message: String) : this(message, false)
|
||||||
|
}
|
219
src/main/kotlin/link/infra/packwiz/installer/ui/InstallWindow.kt
Normal file
219
src/main/kotlin/link/infra/packwiz/installer/ui/InstallWindow.kt
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
package link.infra.packwiz.installer.ui
|
||||||
|
|
||||||
|
import link.infra.packwiz.installer.ui.IExceptionDetails.ExceptionListResult
|
||||||
|
import java.awt.*
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
import java.util.concurrent.Future
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
import javax.swing.*
|
||||||
|
import javax.swing.border.EmptyBorder
|
||||||
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
|
class InstallWindow : IUserInterface {
|
||||||
|
private val frmPackwizlauncher: JFrame
|
||||||
|
private val lblProgresslabel: JLabel
|
||||||
|
private val progressBar: JProgressBar
|
||||||
|
private val btnOptions: JButton
|
||||||
|
|
||||||
|
private var inputStateHandler: InputStateHandler? = null
|
||||||
|
private var title = "Updating modpack..."
|
||||||
|
private var worker: SwingWorkerButWithPublicPublish<Unit, InstallProgress>? = null
|
||||||
|
private val aboutToCrash = AtomicBoolean()
|
||||||
|
|
||||||
|
// TODO: separate JFrame junk from IUserInterface junk?
|
||||||
|
|
||||||
|
init {
|
||||||
|
frmPackwizlauncher = JFrame().apply {
|
||||||
|
title = this@InstallWindow.title
|
||||||
|
setBounds(100, 100, 493, 95)
|
||||||
|
defaultCloseOperation = JFrame.EXIT_ON_CLOSE
|
||||||
|
setLocationRelativeTo(null)
|
||||||
|
|
||||||
|
// Progress bar and loading text
|
||||||
|
add(JPanel().apply {
|
||||||
|
border = EmptyBorder(10, 10, 10, 10)
|
||||||
|
layout = BorderLayout(0, 0)
|
||||||
|
|
||||||
|
progressBar = JProgressBar().apply {
|
||||||
|
isIndeterminate = true
|
||||||
|
}
|
||||||
|
add(progressBar, BorderLayout.CENTER)
|
||||||
|
|
||||||
|
lblProgresslabel = JLabel("Loading...")
|
||||||
|
add(lblProgresslabel, BorderLayout.SOUTH)
|
||||||
|
}, BorderLayout.CENTER)
|
||||||
|
|
||||||
|
// Buttons
|
||||||
|
add(JPanel().apply {
|
||||||
|
border = EmptyBorder(0, 5, 0, 5)
|
||||||
|
layout = GridBagLayout()
|
||||||
|
|
||||||
|
btnOptions = JButton("Optional mods...").apply {
|
||||||
|
alignmentX = Component.CENTER_ALIGNMENT
|
||||||
|
|
||||||
|
addActionListener {
|
||||||
|
text = "Loading..."
|
||||||
|
isEnabled = false
|
||||||
|
inputStateHandler?.pressOptionsButton()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
add(btnOptions, GridBagConstraints().apply {
|
||||||
|
gridx = 0
|
||||||
|
gridy = 0
|
||||||
|
})
|
||||||
|
|
||||||
|
add(JButton("Cancel").apply {
|
||||||
|
addActionListener {
|
||||||
|
isEnabled = false
|
||||||
|
inputStateHandler?.pressCancelButton()
|
||||||
|
}
|
||||||
|
}, GridBagConstraints().apply {
|
||||||
|
gridx = 0
|
||||||
|
gridy = 1
|
||||||
|
})
|
||||||
|
}, BorderLayout.EAST)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun show(handler: InputStateHandler) {
|
||||||
|
inputStateHandler = handler
|
||||||
|
EventQueue.invokeLater {
|
||||||
|
try {
|
||||||
|
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName())
|
||||||
|
frmPackwizlauncher.isVisible = true
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun handleException(e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
EventQueue.invokeLater {
|
||||||
|
JOptionPane.showMessageDialog(null,
|
||||||
|
"An error occurred: \n" + e.javaClass.canonicalName + ": " + e.message,
|
||||||
|
title, JOptionPane.ERROR_MESSAGE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun handleExceptionAndExit(e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
// TODO: Fix this mess
|
||||||
|
// Used to prevent the done() handler of SwingWorker executing if the invokeLater hasn't happened yet
|
||||||
|
aboutToCrash.set(true)
|
||||||
|
EventQueue.invokeLater {
|
||||||
|
JOptionPane.showMessageDialog(null,
|
||||||
|
"A fatal error occurred: \n" + e.javaClass.canonicalName + ": " + e.message,
|
||||||
|
title, JOptionPane.ERROR_MESSAGE)
|
||||||
|
exitProcess(1)
|
||||||
|
}
|
||||||
|
// Pause forever, so it blocks while we wait for System.exit to take effect
|
||||||
|
try {
|
||||||
|
Thread.currentThread().join()
|
||||||
|
} catch (ex: InterruptedException) { // no u
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setTitle(title: String) {
|
||||||
|
this.title = title
|
||||||
|
frmPackwizlauncher.let { frame ->
|
||||||
|
EventQueue.invokeLater { frame.title = title }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun submitProgress(progress: InstallProgress) {
|
||||||
|
val sb = StringBuilder()
|
||||||
|
if (progress.hasProgress) {
|
||||||
|
sb.append('(')
|
||||||
|
sb.append(progress.progress)
|
||||||
|
sb.append('/')
|
||||||
|
sb.append(progress.progressTotal)
|
||||||
|
sb.append(") ")
|
||||||
|
}
|
||||||
|
sb.append(progress.message)
|
||||||
|
// TODO: better logging library?
|
||||||
|
println(sb.toString())
|
||||||
|
worker?.publishPublic(progress)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun executeManager(task: Function0<Unit>) {
|
||||||
|
EventQueue.invokeLater {
|
||||||
|
// TODO: rewrite this stupidity to use channels??!!!
|
||||||
|
worker = object : SwingWorkerButWithPublicPublish<Unit, InstallProgress>() {
|
||||||
|
override fun doInBackground() {
|
||||||
|
task.invoke()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun process(chunks: List<InstallProgress>) {
|
||||||
|
// Only process last chunk
|
||||||
|
if (chunks.isNotEmpty()) {
|
||||||
|
val (message, hasProgress, progress, progressTotal) = chunks[chunks.size - 1]
|
||||||
|
if (hasProgress) {
|
||||||
|
progressBar.isIndeterminate = false
|
||||||
|
progressBar.value = progress
|
||||||
|
progressBar.maximum = progressTotal
|
||||||
|
} else {
|
||||||
|
progressBar.isIndeterminate = true
|
||||||
|
progressBar.value = 0
|
||||||
|
}
|
||||||
|
lblProgresslabel.text = message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun done() {
|
||||||
|
if (aboutToCrash.get()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// TODO: a better way to do this?
|
||||||
|
frmPackwizlauncher.dispose()
|
||||||
|
println("Finished successfully!")
|
||||||
|
exitProcess(0)
|
||||||
|
}
|
||||||
|
}.also {
|
||||||
|
it.execute()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showOptions(options: List<IOptionDetails>): Future<Boolean> {
|
||||||
|
val future = CompletableFuture<Boolean>()
|
||||||
|
EventQueue.invokeLater {
|
||||||
|
OptionsSelectWindow(options, future, frmPackwizlauncher).apply {
|
||||||
|
defaultCloseOperation = JDialog.DISPOSE_ON_CLOSE
|
||||||
|
isVisible = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return future
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showExceptions(exceptions: List<IExceptionDetails>, numTotal: Int, allowsIgnore: Boolean): Future<ExceptionListResult> {
|
||||||
|
val future = CompletableFuture<ExceptionListResult>()
|
||||||
|
EventQueue.invokeLater {
|
||||||
|
ExceptionListWindow(exceptions, future, numTotal, allowsIgnore, frmPackwizlauncher).apply {
|
||||||
|
defaultCloseOperation = JDialog.DISPOSE_ON_CLOSE
|
||||||
|
isVisible = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return future
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun disableOptionsButton() {
|
||||||
|
btnOptions.apply {
|
||||||
|
text = "Optional mods..."
|
||||||
|
isEnabled = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showCancellationDialog(): Future<ExceptionListResult> {
|
||||||
|
val future = CompletableFuture<ExceptionListResult>()
|
||||||
|
EventQueue.invokeLater {
|
||||||
|
val buttons = arrayOf("Quit", "Ignore")
|
||||||
|
val result = JOptionPane.showOptionDialog(frmPackwizlauncher,
|
||||||
|
"The installation was cancelled. Would you like to quit the game, or ignore the update and start the game?",
|
||||||
|
"Cancelled installation",
|
||||||
|
JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, buttons, buttons[0])
|
||||||
|
future.complete(if (result == 0) ExceptionListResult.CANCEL else ExceptionListResult.IGNORE)
|
||||||
|
}
|
||||||
|
return future
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package link.infra.packwiz.installer.ui
|
||||||
|
|
||||||
|
// Serves as a proxy for IOptionDetails, so that setOptionValue isn't called until OK is clicked
|
||||||
|
internal class OptionTempHandler(private val opt: IOptionDetails) : IOptionDetails {
|
||||||
|
override var optionValue = opt.optionValue
|
||||||
|
|
||||||
|
override val name get() = opt.name
|
||||||
|
override val optionDescription get() = opt.optionDescription
|
||||||
|
|
||||||
|
fun finalise() {
|
||||||
|
opt.optionValue = optionValue
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,166 @@
|
|||||||
|
package link.infra.packwiz.installer.ui
|
||||||
|
|
||||||
|
import java.awt.BorderLayout
|
||||||
|
import java.awt.FlowLayout
|
||||||
|
import java.awt.event.ActionEvent
|
||||||
|
import java.awt.event.ActionListener
|
||||||
|
import java.awt.event.WindowAdapter
|
||||||
|
import java.awt.event.WindowEvent
|
||||||
|
import java.util.*
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
import javax.swing.*
|
||||||
|
import javax.swing.border.EmptyBorder
|
||||||
|
import javax.swing.event.TableModelListener
|
||||||
|
import javax.swing.table.TableModel
|
||||||
|
|
||||||
|
class OptionsSelectWindow internal constructor(optList: List<IOptionDetails>, future: CompletableFuture<Boolean>, parentWindow: JFrame?) : JDialog(parentWindow, "Select optional mods...", true), ActionListener {
|
||||||
|
private val lblOptionDescription: JTextArea
|
||||||
|
private val tableModel: OptionTableModel
|
||||||
|
private val future: CompletableFuture<Boolean>
|
||||||
|
|
||||||
|
private class OptionTableModel internal constructor(givenOpts: List<IOptionDetails>) : TableModel {
|
||||||
|
private val opts: List<OptionTempHandler>
|
||||||
|
|
||||||
|
init {
|
||||||
|
val mutOpts = ArrayList<OptionTempHandler>()
|
||||||
|
for (opt in givenOpts) {
|
||||||
|
mutOpts.add(OptionTempHandler(opt))
|
||||||
|
}
|
||||||
|
opts = mutOpts
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getRowCount() = opts.size
|
||||||
|
override fun getColumnCount() = 2
|
||||||
|
|
||||||
|
private val columnNames = arrayOf("Enabled", "Mod name")
|
||||||
|
private val columnTypes = arrayOf(Boolean::class.javaObjectType, String::class.java)
|
||||||
|
private val columnEditables = booleanArrayOf(true, false)
|
||||||
|
|
||||||
|
override fun getColumnName(columnIndex: Int) = columnNames[columnIndex]
|
||||||
|
override fun getColumnClass(columnIndex: Int) = columnTypes[columnIndex]
|
||||||
|
override fun isCellEditable(rowIndex: Int, columnIndex: Int) = columnEditables[columnIndex]
|
||||||
|
|
||||||
|
override fun getValueAt(rowIndex: Int, columnIndex: Int): Any {
|
||||||
|
val opt = opts[rowIndex]
|
||||||
|
return if (columnIndex == 0) opt.optionValue else opt.name
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setValueAt(aValue: Any, rowIndex: Int, columnIndex: Int) {
|
||||||
|
if (columnIndex == 0) {
|
||||||
|
val opt = opts[rowIndex]
|
||||||
|
opt.optionValue = aValue as Boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Noop, the table model doesn't change!
|
||||||
|
override fun addTableModelListener(l: TableModelListener) {}
|
||||||
|
override fun removeTableModelListener(l: TableModelListener) {}
|
||||||
|
|
||||||
|
fun getDescription(rowIndex: Int) = opts[rowIndex].optionDescription
|
||||||
|
|
||||||
|
fun finalise() {
|
||||||
|
for (opt in opts) {
|
||||||
|
opt.finalise()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun actionPerformed(e: ActionEvent) {
|
||||||
|
if (e.actionCommand == "OK") {
|
||||||
|
tableModel.finalise()
|
||||||
|
future.complete(false)
|
||||||
|
dispose()
|
||||||
|
} else if (e.actionCommand == "Cancel") {
|
||||||
|
future.complete(true)
|
||||||
|
dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the dialog.
|
||||||
|
*/
|
||||||
|
init {
|
||||||
|
tableModel = OptionTableModel(optList)
|
||||||
|
this.future = future
|
||||||
|
|
||||||
|
setBounds(100, 100, 450, 300)
|
||||||
|
setLocationRelativeTo(parentWindow)
|
||||||
|
|
||||||
|
contentPane.apply {
|
||||||
|
layout = BorderLayout()
|
||||||
|
add(JPanel().apply {
|
||||||
|
border = EmptyBorder(5, 5, 5, 5)
|
||||||
|
layout = BorderLayout(0, 0)
|
||||||
|
|
||||||
|
add(JSplitPane().apply {
|
||||||
|
resizeWeight = 0.5
|
||||||
|
|
||||||
|
lblOptionDescription = JTextArea("Select an option...").apply {
|
||||||
|
background = UIManager.getColor("List.background")
|
||||||
|
isOpaque = true
|
||||||
|
wrapStyleWord = true
|
||||||
|
lineWrap = true
|
||||||
|
isEditable = false
|
||||||
|
isFocusable = false
|
||||||
|
font = UIManager.getFont("Label.font")
|
||||||
|
border = EmptyBorder(10, 10, 10, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
leftComponent = JScrollPane(JTable().apply {
|
||||||
|
showVerticalLines = false
|
||||||
|
showHorizontalLines = false
|
||||||
|
setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
|
||||||
|
setShowGrid(false)
|
||||||
|
model = tableModel
|
||||||
|
columnModel.getColumn(0).resizable = false
|
||||||
|
columnModel.getColumn(0).preferredWidth = 15
|
||||||
|
columnModel.getColumn(0).maxWidth = 15
|
||||||
|
columnModel.getColumn(1).resizable = false
|
||||||
|
selectionModel.addListSelectionListener {
|
||||||
|
val i = selectedRow
|
||||||
|
if (i > -1) {
|
||||||
|
lblOptionDescription.text = tableModel.getDescription(i)
|
||||||
|
} else {
|
||||||
|
lblOptionDescription.text = "Select an option..."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tableHeader = null
|
||||||
|
}).apply {
|
||||||
|
viewport.background = UIManager.getColor("List.background")
|
||||||
|
horizontalScrollBarPolicy = ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER
|
||||||
|
}
|
||||||
|
|
||||||
|
rightComponent = JScrollPane(lblOptionDescription)
|
||||||
|
})
|
||||||
|
|
||||||
|
add(JPanel().apply {
|
||||||
|
layout = FlowLayout(FlowLayout.RIGHT)
|
||||||
|
|
||||||
|
add(JButton("OK").apply {
|
||||||
|
actionCommand = "OK"
|
||||||
|
addActionListener(this@OptionsSelectWindow)
|
||||||
|
|
||||||
|
this@OptionsSelectWindow.rootPane.defaultButton = this
|
||||||
|
})
|
||||||
|
|
||||||
|
add(JButton("Cancel").apply {
|
||||||
|
actionCommand = "Cancel"
|
||||||
|
addActionListener(this@OptionsSelectWindow)
|
||||||
|
})
|
||||||
|
}, BorderLayout.SOUTH)
|
||||||
|
}, BorderLayout.CENTER)
|
||||||
|
}
|
||||||
|
|
||||||
|
addWindowListener(object : WindowAdapter() {
|
||||||
|
override fun windowClosing(e: WindowEvent) {
|
||||||
|
future.complete(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun windowClosed(e: WindowEvent) {
|
||||||
|
// Just in case closing didn't get triggered - if something else called dispose() the
|
||||||
|
// future will have already completed
|
||||||
|
future.complete(true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package link.infra.packwiz.installer.ui
|
||||||
|
|
||||||
|
import javax.swing.SwingWorker
|
||||||
|
|
||||||
|
// Q: AAA WHAT HAVE YOU DONE THIS IS DISGUSTING
|
||||||
|
// A: it just makes things easier, so i can easily have one interface for CLI/GUI
|
||||||
|
// if someone has a better way to do this please PR it
|
||||||
|
abstract class SwingWorkerButWithPublicPublish<T, V> : SwingWorker<T, V>() {
|
||||||
|
@SafeVarargs
|
||||||
|
fun publishPublic(vararg chunks: V) {
|
||||||
|
publish(*chunks)
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user