Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.minecraft_ta.totalDebugCompanion.catalog;

import com.github.minecraft_ta.totalDebugCompanion.util.Signal;
import com.github.minecraft_ta.totalDebugCompanion.change.ChangeLabels;
import com.github.minecraft_ta.totalDebugCompanion.navigation.NavigationTarget;
import com.github.minecraft_ta.totalDebugCompanion.storage.ChangeRecord;
Expand All @@ -22,8 +23,8 @@ public final class KeyBindingLabels implements ChangeLabels {

/** A key rebound outside Companion, such as in the game, changes the key a row shows. */
@Override
public Runnable follow(Runnable listener) {
return this.keys.assignmentsChanged().subscribe(listener);
public List<Signal> follows() {
return List.of(this.keys.assignmentsChanged());
}

public KeyBindingLabels(KeyBindingControl keys) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.minecraft_ta.totalDebugCompanion.catalog;

import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.nio.file.attribute.FileTime;
import com.github.minecraft_ta.totalDebugCompanion.util.FileWatch;
Expand Down Expand Up @@ -85,6 +86,13 @@ public Signal changed() {
return this.changed;
}

/** The name of the current world as read last, without reading; empty where none was read yet. */
public Optional<String> publishedName() {
Followed now = this.followed;
if (now.reading() == null) return Optional.empty();
return now.reading().published().map(read -> read.saved().name());
}

/** The current world as read last; read now where it was not read yet. Blocking then. */
public World value() {
Followed now = this.followed;
Expand Down Expand Up @@ -128,8 +136,18 @@ private void follow() {
reading.close();
};
this.followed = new Followed(current, reading);
// Told once the new world's first read landed, so its followers find it published, as the tab its name.
Workers.files().execute(() -> {
try {
reading.value();
} catch (IOException unreadable) {
// The World page shows why.
}
this.changed.fire();
});
return;
}
if (before.directory() != null || current != null) this.changed.fire();
if (before.directory() != null) this.changed.fire();
}

/** Watches the datapacks folder of {@code world}, which the game plays, or none; a change there reads it again. */
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.minecraft_ta.totalDebugCompanion.change;

import com.github.minecraft_ta.totalDebugCompanion.util.Signal;
import com.github.minecraft_ta.totalDebugCompanion.catalog.CatalogIndex;
import com.github.minecraft_ta.totalDebugCompanion.navigation.NavigationTarget;
import com.github.minecraft_ta.totalDebugCompanion.storage.ChangeRecord;
Expand Down Expand Up @@ -88,11 +89,11 @@ record Rows(List<Row> rows, List<String> problems) {
CompletableFuture<String> revert(List<ChangeRecord.Change> changes, CatalogIndex index);

/**
* Runs {@code listener} when the values the category's rows show may have changed outside the change record, such as
* a key rebound in the game; returns its removal. Most categories' values change only through the record.
* The signals of values the category's rows show that change outside the change record, such as a key rebound in the
* game. Most categories' values change only through the record.
*/
default Runnable follow(Runnable listener) {
return () -> { };
default List<Signal> follows() {
return List.of();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,6 @@ public ChangesView(EditorContext context) {
context.navigation()::navigate);
}

/** Reads the changed files again. */
public void refresh() {
this.panel.load();
}

@Override
public String getTitle() {
return "Changes";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public DefinitionView(EditorContext context, SubjectRef.Definition subject) {
this.subject = subject;
this.panel = SubjectPanel.definition(subject, new DefinitionDetails.Services(context.project().catalog(),
() -> context.project().sources(), context.itemIcons(), context.navigation()::navigate,
context.project().packs().changed(ChangeRecord.PackSide.RESOURCES)::subscribe));
context.project().packs().changed(ChangeRecord.PackSide.RESOURCES)));
}

public SubjectRef.Definition subject() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public InspectionView(EditorContext context, InspectSubjectPayload subject, Runt
this.subject = Objects.requireNonNull(subject, "subject");
this.panel = SubjectPanel.occurrence(subject, context.snippets(), () -> context.project().scriptFiles(),
new DefinitionDetails.Services(context.project().catalog(), () -> context.project().sources(),
context.itemIcons(), context.navigation()::navigate, context.project().packs().changed(ChangeRecord.PackSide.RESOURCES)::subscribe));
context.itemIcons(), context.navigation()::navigate, context.project().packs().changed(ChangeRecord.PackSide.RESOURCES)));
}

public InspectSubjectPayload subject() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,9 @@ public LogsView(EditorContext context) {
context.navigation()::navigate);
}

/** Lists the logs and crash reports again, selecting {@code file} when it is not null. */
/** Selects {@code file} among the logs and crash reports when it is not null. */
public void show(Path file) {
this.panel.select(file);
this.panel.load();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public final class ModView implements IEditorPanel {
public ModView(EditorContext context, NavigationTarget.ModPage page) {
this.panel = new ModPanel(page.modId(), context.project().catalog(), () -> context.project().sources(),
context.itemIcons(), context.project().profile().workspaceDirectory(), context.project().configSettings(),
context.project().keyBindings(), context.navigation()::navigate, context.project().packs().changed(ChangeRecord.PackSide.RESOURCES)::subscribe);
context.project().keyBindings(), context.navigation()::navigate, context.project().packs().changed(ChangeRecord.PackSide.RESOURCES));
this.panel.show(page);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@ public PackConfigurationView(EditorContext context) {
}

/** Reads the configuration files again. */
public void refresh() {
this.panel.load();
}

@Override
public String getTitle() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ private CompletableFuture<Void> performNavigation(NavigationTarget target, Activ
PackConfigurationView.class,
view -> true,
() -> new PackConfigurationView(editors.get())
).thenAccept(PackConfigurationView::refresh), activation);
).thenAccept(view -> { }), activation);
case NavigationTarget.PackResources resources -> dispatchNavigation(() -> this.tabs.focusOrCreateIfAbsent(
PackResourcesView.class,
view -> true,
Expand All @@ -332,7 +332,7 @@ private CompletableFuture<Void> performNavigation(NavigationTarget target, Activ
ChangesView.class,
view -> true,
() -> new ChangesView(editors.get())
).thenAccept(ChangesView::refresh), activation);
).thenAccept(view -> { }), activation);
case NavigationTarget.Content content -> dispatchNavigation(() -> this.tabs.focusOrCreateIfAbsent(
ContentView.class,
view -> true,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.github.minecraft_ta.totalDebugCompanion.ui.components;

import com.github.minecraft_ta.totalDebugCompanion.util.Signal;
import com.github.minecraft_ta.totalDebugCompanion.util.Workers;

import javax.swing.JComponent;
import javax.swing.SwingUtilities;
Expand All @@ -17,7 +18,6 @@
import java.util.concurrent.CompletionException;
import java.util.function.BooleanSupplier;
import java.util.function.Consumer;
import java.util.function.Function;

/**
* Reads what a page shows off the Swing thread and shows it on that thread (docs/SYSTEMS.md, section 3). One read runs
Expand All @@ -29,10 +29,8 @@
* first shown, and after a followed signal fires, at once while the page is shown, otherwise once it is shown again. A
* page shown again with nothing changed reads nothing, unless its last read failed. While the page holds its reads
* ({@link #hold}), as during a save, signals wait in the same way. The page never reads in its constructor or because a
* navigation showed it.</p>
*
* <p>Pages not moved to {@link #page} yet use the older modes {@link #whenShown}, {@link #waitsWhileHidden} and
* {@link #readsWhenShown} with {@link #follow}, and read in their constructors; the last of them to move deletes those.</p>
* navigation showed it. A page whose files others write and only it reads, such as the logs, reads whenever it is shown
* ({@link #readsWhenShown}); work that only redraws from memory waits the same way ({@link #updates}).</p>
*/
public final class PageLoader<T> {
/** What to read: prepared on the Swing thread, where the page's state is captured, then run off it. */
Expand Down Expand Up @@ -78,6 +76,11 @@ public interface Read<T> {
*/
private boolean showReadQueued;

/** A loader for {@code page}, which reads nothing and only redraws from memory ({@link #updates}). */
public static PageLoader<Void> redraws(JComponent page) {
return new PageLoader<Void>(() -> null, nothing -> { }, failure -> { }).page(page);
}

/** {@code show} and {@code fail} run on the Swing thread with what a read returned or why it failed. */
public PageLoader(Read<T> read, Consumer<T> show, Consumer<Throwable> fail) {
this.read = Objects.requireNonNull(read, "read");
Expand Down Expand Up @@ -125,24 +128,6 @@ public boolean fired(Signal signal) {
return this.fired.contains(signal);
}

/**
* Waits while {@code page} is hidden and reads every time it is shown, since what it shows may have changed while it
* was hidden without a source telling, such as a file the game writes.
*/
public PageLoader<T> whenShown(JComponent page) {
this.page = Objects.requireNonNull(page, "page");
return readsWhenShown(page);
}

/**
* Waits while {@code page} is hidden: a followed change then reads once the page is shown again, and not at all when
* none came. For pages whose sources tell every change.
*/
public PageLoader<T> waitsWhileHidden(JComponent page) {
this.page = Objects.requireNonNull(page, "page");
return watch(page, false);
}

/**
* Reads every time {@code component} is shown, such as a tab listing files that change without telling. It may be a
* part of the page, which then reads when that part is chosen.
Expand All @@ -169,11 +154,39 @@ private PageLoader<T> watch(JComponent component, boolean always) {
}

/**
* Loads whenever a source changes, or once the page is shown again. {@code subscribe} adds a listener to the source
* and returns what removes it, as {@code catalog.changed()::subscribe} does; the listener may be called on any thread.
* Runs {@code update}, which only redraws from memory, such as icons again after new ones came, when {@code signal}
* fires: at once while the page is shown, otherwise once it is shown again, once however often it fired. Swing thread.
*/
public PageLoader<T> follow(Function<Runnable, Runnable> subscribe) {
this.unsubscribe.add(subscribe.apply(() -> SwingUtilities.invokeLater(() -> changed(ALWAYS))));
public PageLoader<T> updates(Signal signal, Runnable update) {
Objects.requireNonNull(update, "update");
boolean[] missed = {false};
this.unsubscribe.add(signal.subscribe(() -> SwingUtilities.invokeLater(() -> {
if (this.disposed) return;
if (this.page != null && !this.page.isShowing()) missed[0] = true;
else update.run();
})));
if (this.page != null) {
JComponent shown = this.page;
HierarchyListener listener = event -> {
if ((event.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) == 0 || !shown.isShowing() || !missed[0]) return;
missed[0] = false;
if (!this.disposed) update.run();
};
shown.addHierarchyListener(listener);
this.unwatch.add(() -> shown.removeHierarchyListener(listener));
}
return this;
}

/**
* Runs {@code redraw} on the Swing thread whenever {@code signal} fires, shown or not, for what shows outside the page
* from memory, such as the title of its tab.
*/
public PageLoader<T> retitles(Signal signal, Runnable redraw) {
Objects.requireNonNull(redraw, "redraw");
this.unsubscribe.add(signal.subscribe(() -> SwingUtilities.invokeLater(() -> {
if (!this.disposed) redraw.run();
})));
return this;
}

Expand Down Expand Up @@ -253,7 +266,7 @@ public void load() {
} catch (Exception exception) {
throw new CompletionException(exception);
}
}).whenComplete((value, failure) -> {
}, Workers.files()).whenComplete((value, failure) -> {
// Completed off the Swing thread, so a wait on the Swing thread does not block what it waits for.
finished.complete(null);
SwingUtilities.invokeLater(() -> showRead(value, failure));
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -223,9 +223,10 @@ public ChangesPanel(PackCatalogService catalog, ChangeRecord record, List<Change

this.loader = new PageLoader<>(this::prepareLoad, this::show,
failure -> setStatus("Could not read the changes: " + failure.getMessage()))
.whenShown(this).follow(this.record.changed()::subscribe).follow(this.catalog.changed()::subscribe);
for (ChangeLabels labels : categories) this.loader.follow(labels::follow);
load();
.page(this).follows(this.record.changed()).follows(this.catalog.changed())
// Until the owners notice values put back outside Companion, the labels read the files whenever shown.
.readsWhenShown(this);
for (ChangeLabels labels : categories) labels.follows().forEach(this.loader::follows);
}

public void load() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@ public ConfigFilePanel(Path file, ConfigSources.Owner owner, ConfigSettings conf
add(bar, BorderLayout.NORTH);
add(this.editor.component(), BorderLayout.CENTER);
this.loader = new PageLoader<>(() -> this::readFile, this.editor::load,
failure -> setStatus("Could not read " + this.file.getFileName() + ": " + failure.getMessage())).whenShown(this);
load();
failure -> setStatus("Could not read " + this.file.getFileName() + ": " + failure.getMessage())).page(this).readsWhenShown(this);
}

public Path file() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,15 +107,15 @@ final class ConfigPanel extends JPanel {
this.navigator = Objects.requireNonNull(navigator, "navigator");
this.values = new PageLoader<>(this::prepareValues, read -> show(read.file(), read.values(),
read.values() == null ? "" : read.values().text(), read.problem()),
failure -> showFailure("Could not read the file: " + failure.getMessage())).whenShown(this);
failure -> showFailure("Could not read the file: " + failure.getMessage())).page(this).readsWhenShown(this);
this.sources = new PageLoader<>(this::prepareSources, read -> showSources(read.file(), read.found(), true),
failure -> showFailure("Could not list the worlds' copies: " + failure.getMessage()))
// A server configuration is shown from the world the game has open first; it follows the game to another
// world, unless its text has unsaved changes, which stay with the file they were made in.
.follow(listener -> this.location.playingChanged().subscribe(() -> SwingUtilities.invokeLater(() -> {
.page(this).follows(this.location.playingChanged(), () -> {
PackCatalog.ConfigFile file = selectedFile();
if (file != null && file.type() == PackCatalog.ConfigType.SERVER && !this.textEditor.modified()) listener.run();
})));
return file != null && file.type() == PackCatalog.ConfigType.SERVER && !this.textEditor.modified();
});
configureFiles();
this.content.add(toolbar(), BorderLayout.NORTH);
JPanel settings = new JPanel(new BorderLayout());
Expand Down
Loading