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.ListeningExecutorService;
import com.skcraft.concurrency.DefaultProgress;
import com.skcraft.concurrency.ObservableFuture;
import com.skcraft.launcher.creator.model.creator.Pack;
import com.skcraft.launcher.creator.plugin.MenuContext;
import com.skcraft.launcher.creator.plugin.PluginMenu;
@ -28,6 +29,7 @@ import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
@ -39,14 +41,17 @@ public class CurseModsDialog extends JDialog {
private final CursePack cursePack;
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 JButton searchButton = new JButton("Search");
private final JProgressBar progressBar = new JProgressBar(0, 1000);
private final JList<CurseProject> searchPane = 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 JButton addMod = new JButton("Add Mods >>");
private final JButton removeMod = new JButton("<< Remove Mods");
private final JButton done = new JButton("Done");
private Timer activeTimer;
private final CurseProjectListRenderer projectRenderer = new CurseProjectListRenderer();
@ -94,10 +99,17 @@ public class CurseModsDialog extends JDialog {
buttonsPanel.addElement(removeMod);
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(splitPane, BorderLayout.CENTER);
panel.add(buttonsPanel, BorderLayout.SOUTH);
panel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0));
add(panel);
@ -105,24 +117,41 @@ public class CurseModsDialog extends JDialog {
searchButton.addActionListener(e -> search());
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);
});
removeMod.addActionListener(e -> {
ListenableFuture<Object> future = executor.submit(
cursePack.removeMany(selectedPane.getSelectedValuesList()));
CursePack.RemoveModsCall call = cursePack.removeMany(selectedPane.getSelectedValuesList());
ListenableFuture<Object> future = executor.submit(call);
setProgressItem(new ObservableFuture<>(future, call));
SwingHelper.addErrorDialogCallback(getOwner(), future);
});
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
public void dispose() {
if (activeTimer != null) {
activeTimer.stop();
}
// Let's make sure the image cache is emptied
projectRenderer.clearCache();
super.dispose();
@ -173,16 +202,24 @@ public class CurseModsDialog extends JDialog {
}
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() {
this.cache = new HashMap<>();
this.pending = new HashSet<>();
}
private BufferedImage getCachedIcon(String imgUrl, Runnable cb) {
if (!cache.containsKey(imgUrl)) {
if (pending.contains(imgUrl)) {
return null;
}
pending.add(imgUrl);
ImageWorker.downloadImage(imgUrl, img -> {
cache.put(imgUrl, img);
pending.remove(imgUrl);
cb.run();
});
@ -261,9 +298,11 @@ public class CurseModsDialog extends JDialog {
ListenableFuture<?> future = ctx.getExecutor().submit(() -> {
File target = new File(pack.getDirectory(), "cursemods");
if (target.isDirectory()) {
scanner.walk(target);
if (!target.isDirectory()) {
target.mkdirs();
}
scanner.walk(target);
return null;
});

View File

@ -1,6 +1,8 @@
package com.skcraft.plugin.curse.creator;
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.SettableProgress;
import com.skcraft.launcher.creator.model.creator.Pack;
@ -18,13 +20,13 @@ import java.util.concurrent.Callable;
public class CursePack {
@Getter private final CurseSearchResults searchResults = new CurseSearchResults();
@Getter private final AddedModList modList = new AddedModList();
private final ObjectMapper mapper;
private final ObjectWriter writer;
private final String gameVersion;
private final File curseModsDir;
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.curseModsDir = new File(pack.getDirectory(), "cursemods");
}
@ -41,7 +43,7 @@ public class CursePack {
File target = loadedMod.getDiskLocation(curseModsDir);
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 {
@ -63,7 +65,7 @@ public class CursePack {
}
@RequiredArgsConstructor
private class AddModsCall implements Callable<Object>, ProgressObservable {
class AddModsCall implements Callable<Object>, ProgressObservable {
private final List<CurseProject> projects;
private final SettableProgress progress = new SettableProgress("", -1);
@ -73,7 +75,7 @@ public class CursePack {
int total = projects.size();
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);
current++;
@ -94,7 +96,7 @@ public class CursePack {
}
@RequiredArgsConstructor
private class RemoveModsCall implements Callable<Object>, ProgressObservable {
class RemoveModsCall implements Callable<Object>, ProgressObservable {
private final List<AddedMod> mods;
private final SettableProgress progress = new SettableProgress("", -1);
@ -105,7 +107,7 @@ public class CursePack {
for (AddedMod mod : mods) {
progress.set(String.format("Removing mod %s", mod.getProject().getName()),
100 * (double) current / total);
(double) current / total);
removeMod(mod);
current++;