Implement sorting

This commit is contained in:
crschnick 2023-08-11 18:52:05 +00:00
parent 202f9e4939
commit 473974ada9
7 changed files with 209 additions and 16 deletions

View file

@ -12,7 +12,7 @@ import javafx.scene.control.ScrollPane;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
@ -34,7 +34,7 @@ public class ListBoxViewComp<T> extends Comp<CompStructure<ScrollPane>> {
@Override
public CompStructure<ScrollPane> createBase() {
Map<T, Region> cache = new HashMap<>();
Map<T, Region> cache = new IdentityHashMap<>();
VBox vbox = new VBox();
vbox.getStyleClass().add("content");

View file

@ -0,0 +1,112 @@
package io.xpipe.app.comp.storage.store;
import io.xpipe.app.fxcomps.Comp;
import io.xpipe.app.fxcomps.SimpleComp;
import io.xpipe.app.fxcomps.impl.FancyTooltipAugment;
import io.xpipe.app.fxcomps.impl.HorizontalComp;
import io.xpipe.app.fxcomps.impl.IconButtonComp;
import javafx.beans.binding.Bindings;
import javafx.beans.property.Property;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination;
import javafx.scene.layout.Region;
import java.util.List;
public class StoreOrganizationComp extends SimpleComp {
private final Property<StoreSortMode> sortMode;
public StoreOrganizationComp() {
this.sortMode = StoreViewState.get().getSortMode();
}
private Comp<?> createAlphabeticalSortButton() {
var icon = Bindings.createStringBinding(
() -> {
if (sortMode.getValue() == StoreSortMode.ALPHABETICAL_ASC) {
return "mdi2s-sort-alphabetical-descending";
}
if (sortMode.getValue() == StoreSortMode.ALPHABETICAL_DESC) {
return "mdi2s-sort-alphabetical-ascending";
}
return "mdi2s-sort-alphabetical-descending";
},
sortMode);
var alphabetical = new IconButtonComp(icon, () -> {
if (sortMode.getValue() == StoreSortMode.ALPHABETICAL_ASC) {
sortMode.setValue(StoreSortMode.ALPHABETICAL_DESC);
} else if (sortMode.getValue() == StoreSortMode.ALPHABETICAL_DESC) {
sortMode.setValue(StoreSortMode.ALPHABETICAL_ASC);
} else {
sortMode.setValue(StoreSortMode.ALPHABETICAL_ASC);
}
});
alphabetical.apply(alphabeticalR -> {
alphabeticalR
.get()
.opacityProperty()
.bind(Bindings.createDoubleBinding(
() -> {
if (sortMode.getValue() == StoreSortMode.ALPHABETICAL_ASC
|| sortMode.getValue() == StoreSortMode.ALPHABETICAL_DESC) {
return 1.0;
}
return 0.4;
},
sortMode));
});
alphabetical.apply(new FancyTooltipAugment<>("sortAlphabetical"));
alphabetical.shortcut(new KeyCodeCombination(KeyCode.P, KeyCombination.SHORTCUT_DOWN));
return alphabetical;
}
private Comp<?> createDateSortButton() {
var icon = Bindings.createStringBinding(
() -> {
if (sortMode.getValue() == StoreSortMode.DATE_ASC) {
return "mdi2s-sort-clock-ascending-outline";
}
if (sortMode.getValue() == StoreSortMode.DATE_DESC) {
return "mdi2s-sort-clock-descending-outline";
}
return "mdi2s-sort-clock-ascending-outline";
},
sortMode);
var date = new IconButtonComp(icon, () -> {
if (sortMode.getValue() == StoreSortMode.DATE_ASC) {
sortMode.setValue(StoreSortMode.DATE_DESC);
} else if (sortMode.getValue() == StoreSortMode.DATE_DESC) {
sortMode.setValue(StoreSortMode.DATE_ASC);
} else {
sortMode.setValue(StoreSortMode.DATE_ASC);
}
});
date.apply(dateR -> {
dateR.get()
.opacityProperty()
.bind(Bindings.createDoubleBinding(
() -> {
if (sortMode.getValue() == StoreSortMode.DATE_ASC
|| sortMode.getValue() == StoreSortMode.DATE_DESC) {
return 1.0;
}
return 0.4;
},
sortMode));
});
date.apply(new FancyTooltipAugment<>("sortLastUsed"));
date.shortcut(new KeyCodeCombination(KeyCode.L, KeyCombination.SHORTCUT_DOWN));
return date;
}
private Comp<?> createSortButtonBar() {
return new HorizontalComp(List.of(createDateSortButton(), createAlphabeticalSortButton()));
}
@Override
protected Region createSimple() {
return createSortButtonBar().styleClass("bar").prefHeight(40).createRegion();
}
}

View file

@ -8,11 +8,11 @@ import io.xpipe.app.storage.DataStoreEntry;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ObservableBooleanValue;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import lombok.Value;
import java.time.Instant;
import java.util.Comparator;
@Value
@ -32,6 +32,12 @@ public class StoreSection implements StorageFilter.Filterable {
int depth;
ObservableBooleanValue showDetails;
static ObservableValue<Comparator<StoreSection>> sortMode = Bindings.createObjectBinding(() -> {
return Comparator.<StoreSection>comparingInt(value -> value.getWrapper().getEntry().getState().isUsable() ? 1 : -1)
.thenComparing(StoreViewState.get().getSortMode().getValue().comparator());
}, StoreViewState.get().getSortMode());
public StoreSection(StoreEntryWrapper wrapper, ObservableList<StoreSection> children, int depth) {
this.wrapper = wrapper;
this.children = children;
@ -48,14 +54,6 @@ public class StoreSection implements StorageFilter.Filterable {
}
}
private static final Comparator<StoreSection> COMPARATOR = Comparator.<StoreSection, Instant>comparing(
o -> o.wrapper.getEntry().getState().isUsable()
? o.wrapper.getEntry().getLastModified()
: Instant.EPOCH)
.reversed()
.thenComparing(
storeEntrySection -> storeEntrySection.wrapper.getEntry().getName());
public static StoreSection createTopLevel() {
var topLevel = BindingsHelper.cachedMappedContentBinding(
StoreViewState.get().getAllEntries(), storeEntryWrapper -> create(storeEntryWrapper, 1));
@ -64,7 +62,7 @@ public class StoreSection implements StorageFilter.Filterable {
.getParent(section.getWrapper().getEntry(), true)
.isEmpty();
});
var ordered = BindingsHelper.orderedContentBinding(filtered, COMPARATOR);
var ordered = BindingsHelper.orderedContentBinding(filtered, sortMode);
return new StoreSection(null, ordered, 0);
}
@ -81,7 +79,7 @@ public class StoreSection implements StorageFilter.Filterable {
.orElse(false);
});
var children = BindingsHelper.cachedMappedContentBinding(filtered, entry1 -> create(entry1, depth + 1));
var ordered = BindingsHelper.orderedContentBinding(children, COMPARATOR);
var ordered = BindingsHelper.orderedContentBinding(children, sortMode);
return new StoreSection(e, ordered, depth);
}

View file

@ -10,13 +10,15 @@ import javafx.scene.layout.VBox;
import java.util.List;
public class StoreSidebarComp extends SimpleComp {
@Override
protected Region createSimple() {
var sideBar = new VerticalComp(List.of(
new StoreEntryListHeaderComp(),
new StoreCreationBarComp(),
new StoreOrganizationComp(),
Comp.of(() -> new Region()).styleClass("bar").styleClass("filler-bar")));
sideBar.apply(s -> VBox.setVgrow(s.get().getChildren().get(2), Priority.ALWAYS));
sideBar.apply(s -> VBox.setVgrow(s.get().getChildren().get(3), Priority.ALWAYS));
sideBar.styleClass("sidebar");
return sideBar.createRegion();
}

View file

@ -0,0 +1,65 @@
package io.xpipe.app.comp.storage.store;
import java.time.Instant;
import java.util.Comparator;
import java.util.Locale;
public interface StoreSortMode {
static StoreSortMode ALPHABETICAL_DESC = new StoreSortMode() {
@Override
public String getId() {
return "alphabetical-desc";
}
@Override
public Comparator<StoreSection> comparator() {
return Comparator.<StoreSection, String>comparing(
e -> e.getWrapper().getName().toLowerCase(Locale.ROOT));
}
};
static StoreSortMode ALPHABETICAL_ASC = new StoreSortMode() {
@Override
public String getId() {
return "alphabetical-asc";
}
@Override
public Comparator<StoreSection> comparator() {
return Comparator.<StoreSection, String>comparing(
e -> e.getWrapper().getName().toLowerCase(Locale.ROOT))
.reversed();
}
};
static StoreSortMode DATE_DESC = new StoreSortMode() {
@Override
public String getId() {
return "date-desc";
}
@Override
public Comparator<StoreSection> comparator() {
return Comparator.<StoreSection, Instant>comparing(
e -> e.getWrapper().getLastAccess());
}
};
static StoreSortMode DATE_ASC = new StoreSortMode() {
@Override
public String getId() {
return "date-asc";
}
@Override
public Comparator<StoreSection> comparator() {
return Comparator.<StoreSection, Instant>comparing(e -> e.getWrapper().getLastAccess())
.reversed();
}
};
String getId();
Comparator<StoreSection> comparator();
}

View file

@ -1,15 +1,18 @@
package io.xpipe.app.comp.storage.store;
import io.xpipe.app.comp.storage.StorageFilter;
import io.xpipe.app.core.AppCache;
import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.app.storage.StorageListener;
import javafx.application.Platform;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import lombok.Getter;
import java.time.Instant;
import java.util.Arrays;
@ -28,7 +31,17 @@ public class StoreViewState {
private final ObservableList<StoreEntryWrapper> shownEntries =
FXCollections.observableList(new CopyOnWriteArrayList<>());
@Getter
private final Property<StoreSortMode> sortMode;
private StoreViewState() {
var val = AppCache.getIfPresent("sortMode", StoreSortMode.class).orElse(StoreSortMode.ALPHABETICAL_DESC);
this.sortMode = new SimpleObjectProperty<>(val);
this.sortMode.addListener((observable, oldValue, newValue) -> {
AppCache.update("sortMode", newValue.getId());
});
try {
addStorageGroupListeners();
addShownContentChangeListeners();

View file

@ -142,15 +142,18 @@ public class BindingsHelper {
return l1;
}
public static <V> ObservableList<V> orderedContentBinding(ObservableList<V> l2, Comparator<V> comp) {
public static <V> ObservableList<V> orderedContentBinding(ObservableList<V> l2, ObservableValue<Comparator<V>> comp) {
ObservableList<V> l1 = FXCollections.observableList(new ArrayList<>());
Runnable runnable = () -> {
setContent(l1, l2.stream().sorted(comp).toList());
setContent(l1, l2.stream().sorted(comp.getValue()).toList());
};
runnable.run();
l2.addListener((ListChangeListener<? super V>) c -> {
runnable.run();
});
comp.addListener((observable, oldValue, newValue) -> {
runnable.run();
});
linkPersistently(l2, l1);
return l1;
}