1
0
mirror of https://github.com/SKCraft/Launcher.git synced 2024-11-27 12:46:22 +01:00

Add progress bars

This commit is contained in:
Henry Le Grys 2021-05-24 10:05:15 +01:00
parent badd91d2b8
commit 9640cd8dcf
2 changed files with 56 additions and 15 deletions

View File

@ -6,6 +6,7 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.ListeningExecutorService;
import com.skcraft.concurrency.DefaultProgress; import com.skcraft.concurrency.DefaultProgress;
import com.skcraft.concurrency.ObservableFuture;
import com.skcraft.launcher.creator.model.creator.Pack; import com.skcraft.launcher.creator.model.creator.Pack;
import com.skcraft.launcher.creator.plugin.MenuContext; import com.skcraft.launcher.creator.plugin.MenuContext;
import com.skcraft.launcher.creator.plugin.PluginMenu; import com.skcraft.launcher.creator.plugin.PluginMenu;
@ -28,6 +29,7 @@ import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.File; import java.io.File;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.function.Consumer; import java.util.function.Consumer;
@ -39,14 +41,17 @@ public class CurseModsDialog extends JDialog {
private final CursePack cursePack; private final CursePack cursePack;
private final JPanel panel = new JPanel(new BorderLayout(0, 5)); private final JPanel panel = new JPanel(new BorderLayout(0, 5));
private final JLabel title = new JLabel("Search for mods on CurseForge:");
private final JTextField searchBox = new JTextField(); private final JTextField searchBox = new JTextField();
private final JButton searchButton = new JButton("Search"); private final JButton searchButton = new JButton("Search");
private final JProgressBar progressBar = new JProgressBar(0, 1000);
private final JList<CurseProject> searchPane = new JList<>(); private final JList<CurseProject> searchPane = new JList<>();
private final JList<AddedMod> selectedPane = new JList<>(); private final JList<AddedMod> selectedPane = new JList<>();
private final JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, new JScrollPane(searchPane), new JScrollPane(selectedPane)); private final JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, new JScrollPane(searchPane), new JScrollPane(selectedPane));
private final JButton addMod = new JButton("Add Mods >>"); private final JButton addMod = new JButton("Add Mods >>");
private final JButton removeMod = new JButton("<< Remove Mods"); private final JButton removeMod = new JButton("<< Remove Mods");
private final JButton done = new JButton("Done"); private final JButton done = new JButton("Done");
private Timer activeTimer;
private final CurseProjectListRenderer projectRenderer = new CurseProjectListRenderer(); private final CurseProjectListRenderer projectRenderer = new CurseProjectListRenderer();
@ -94,10 +99,17 @@ public class CurseModsDialog extends JDialog {
buttonsPanel.addElement(removeMod); buttonsPanel.addElement(removeMod);
buttonsPanel.setAlignmentX(CENTER_ALIGNMENT); buttonsPanel.setAlignmentX(CENTER_ALIGNMENT);
panel.add(searchBarPanel, BorderLayout.NORTH); LinedBoxPanel topPanel = new LinedBoxPanel(false);
topPanel.addElement(title);
topPanel.addElement(searchBarPanel);
topPanel.addElement(progressBar);
topPanel.setAlignmentX(CENTER_ALIGNMENT);
panel.add(topPanel, BorderLayout.NORTH);
panel.add(Box.createVerticalStrut(5)); panel.add(Box.createVerticalStrut(5));
panel.add(splitPane, BorderLayout.CENTER); panel.add(splitPane, BorderLayout.CENTER);
panel.add(buttonsPanel, BorderLayout.SOUTH); panel.add(buttonsPanel, BorderLayout.SOUTH);
panel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0));
add(panel); add(panel);
@ -105,24 +117,41 @@ public class CurseModsDialog extends JDialog {
searchButton.addActionListener(e -> search()); searchButton.addActionListener(e -> search());
addMod.addActionListener(e -> { addMod.addActionListener(e -> {
ListenableFuture<Object> future = executor.submit(cursePack.addMany(searchPane.getSelectedValuesList())); CursePack.AddModsCall call = cursePack.addMany(searchPane.getSelectedValuesList());
ListenableFuture<Object> future = executor.submit(call);
// TODO: Update a progress bar built in to the dialog. setProgressItem(new ObservableFuture<>(future, call));
SwingHelper.addErrorDialogCallback(getOwner(), future); SwingHelper.addErrorDialogCallback(getOwner(), future);
}); });
removeMod.addActionListener(e -> { removeMod.addActionListener(e -> {
ListenableFuture<Object> future = executor.submit( CursePack.RemoveModsCall call = cursePack.removeMany(selectedPane.getSelectedValuesList());
cursePack.removeMany(selectedPane.getSelectedValuesList())); ListenableFuture<Object> future = executor.submit(call);
setProgressItem(new ObservableFuture<>(future, call));
SwingHelper.addErrorDialogCallback(getOwner(), future); SwingHelper.addErrorDialogCallback(getOwner(), future);
}); });
done.addActionListener(e -> dispose()); done.addActionListener(e -> dispose());
} }
private void setProgressItem(ObservableFuture<?> observable) {
if (activeTimer != null) {
activeTimer.stop();
}
activeTimer = new Timer(400, e -> progressBar.setValue((int) (1000 * observable.getProgress())));
activeTimer.setInitialDelay(0);
activeTimer.start();
observable.addListener(() -> activeTimer.stop(), SwingExecutor.INSTANCE);
}
@Override @Override
public void dispose() { public void dispose() {
if (activeTimer != null) {
activeTimer.stop();
}
// Let's make sure the image cache is emptied // Let's make sure the image cache is emptied
projectRenderer.clearCache(); projectRenderer.clearCache();
super.dispose(); super.dispose();
@ -173,16 +202,24 @@ public class CurseModsDialog extends JDialog {
} }
private static class CurseProjectListRenderer extends JLabel implements ListCellRenderer<ProjectHolder> { private static class CurseProjectListRenderer extends JLabel implements ListCellRenderer<ProjectHolder> {
private HashMap<String, BufferedImage> cache; private final HashMap<String, BufferedImage> cache;
private final HashSet<String> pending;
public CurseProjectListRenderer() { public CurseProjectListRenderer() {
this.cache = new HashMap<>(); this.cache = new HashMap<>();
this.pending = new HashSet<>();
} }
private BufferedImage getCachedIcon(String imgUrl, Runnable cb) { private BufferedImage getCachedIcon(String imgUrl, Runnable cb) {
if (!cache.containsKey(imgUrl)) { if (!cache.containsKey(imgUrl)) {
if (pending.contains(imgUrl)) {
return null;
}
pending.add(imgUrl);
ImageWorker.downloadImage(imgUrl, img -> { ImageWorker.downloadImage(imgUrl, img -> {
cache.put(imgUrl, img); cache.put(imgUrl, img);
pending.remove(imgUrl);
cb.run(); cb.run();
}); });
@ -261,9 +298,11 @@ public class CurseModsDialog extends JDialog {
ListenableFuture<?> future = ctx.getExecutor().submit(() -> { ListenableFuture<?> future = ctx.getExecutor().submit(() -> {
File target = new File(pack.getDirectory(), "cursemods"); File target = new File(pack.getDirectory(), "cursemods");
if (target.isDirectory()) { if (!target.isDirectory()) {
scanner.walk(target); target.mkdirs();
} }
scanner.walk(target);
return null; return null;
}); });

View File

@ -1,6 +1,8 @@
package com.skcraft.plugin.curse.creator; package com.skcraft.plugin.curse.creator;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.skcraft.concurrency.ProgressObservable; import com.skcraft.concurrency.ProgressObservable;
import com.skcraft.concurrency.SettableProgress; import com.skcraft.concurrency.SettableProgress;
import com.skcraft.launcher.creator.model.creator.Pack; import com.skcraft.launcher.creator.model.creator.Pack;
@ -18,13 +20,13 @@ import java.util.concurrent.Callable;
public class CursePack { public class CursePack {
@Getter private final CurseSearchResults searchResults = new CurseSearchResults(); @Getter private final CurseSearchResults searchResults = new CurseSearchResults();
@Getter private final AddedModList modList = new AddedModList(); @Getter private final AddedModList modList = new AddedModList();
private final ObjectMapper mapper; private final ObjectWriter writer;
private final String gameVersion; private final String gameVersion;
private final File curseModsDir; private final File curseModsDir;
public CursePack(ObjectMapper mapper, Pack pack) { public CursePack(ObjectMapper mapper, Pack pack) {
this.mapper = mapper; this.writer = mapper.writerWithDefaultPrettyPrinter().without(SerializationFeature.WRITE_NULL_MAP_VALUES);
this.gameVersion = pack.getCachedConfig().getGameVersion(); this.gameVersion = pack.getCachedConfig().getGameVersion();
this.curseModsDir = new File(pack.getDirectory(), "cursemods"); this.curseModsDir = new File(pack.getDirectory(), "cursemods");
} }
@ -41,7 +43,7 @@ public class CursePack {
File target = loadedMod.getDiskLocation(curseModsDir); File target = loadedMod.getDiskLocation(curseModsDir);
log.info(String.format("Saving mod %s", target.getName())); log.info(String.format("Saving mod %s", target.getName()));
mapper.writeValue(target, loadedMod.getMod()); writer.writeValue(target, loadedMod.getMod());
} }
public void removeMod(AddedMod mod) throws IOException { public void removeMod(AddedMod mod) throws IOException {
@ -63,7 +65,7 @@ public class CursePack {
} }
@RequiredArgsConstructor @RequiredArgsConstructor
private class AddModsCall implements Callable<Object>, ProgressObservable { class AddModsCall implements Callable<Object>, ProgressObservable {
private final List<CurseProject> projects; private final List<CurseProject> projects;
private final SettableProgress progress = new SettableProgress("", -1); private final SettableProgress progress = new SettableProgress("", -1);
@ -73,7 +75,7 @@ public class CursePack {
int total = projects.size(); int total = projects.size();
for (CurseProject project : projects) { for (CurseProject project : projects) {
progress.set(String.format("Adding mod %s", project.getName()), 100 * (double) current / total); progress.set(String.format("Adding mod %s", project.getName()), (double) current / total);
addMod(project); addMod(project);
current++; current++;
@ -94,7 +96,7 @@ public class CursePack {
} }
@RequiredArgsConstructor @RequiredArgsConstructor
private class RemoveModsCall implements Callable<Object>, ProgressObservable { class RemoveModsCall implements Callable<Object>, ProgressObservable {
private final List<AddedMod> mods; private final List<AddedMod> mods;
private final SettableProgress progress = new SettableProgress("", -1); private final SettableProgress progress = new SettableProgress("", -1);
@ -105,7 +107,7 @@ public class CursePack {
for (AddedMod mod : mods) { for (AddedMod mod : mods) {
progress.set(String.format("Removing mod %s", mod.getProject().getName()), progress.set(String.format("Removing mod %s", mod.getProject().getName()),
100 * (double) current / total); (double) current / total);
removeMod(mod); removeMod(mod);
current++; current++;