mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-22 23:50:32 +00:00
Implement sorting
This commit is contained in:
parent
202f9e4939
commit
473974ada9
7 changed files with 209 additions and 16 deletions
|
@ -12,7 +12,7 @@ import javafx.scene.control.ScrollPane;
|
||||||
import javafx.scene.layout.Region;
|
import javafx.scene.layout.Region;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.IdentityHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
@ -34,7 +34,7 @@ public class ListBoxViewComp<T> extends Comp<CompStructure<ScrollPane>> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompStructure<ScrollPane> createBase() {
|
public CompStructure<ScrollPane> createBase() {
|
||||||
Map<T, Region> cache = new HashMap<>();
|
Map<T, Region> cache = new IdentityHashMap<>();
|
||||||
|
|
||||||
VBox vbox = new VBox();
|
VBox vbox = new VBox();
|
||||||
vbox.getStyleClass().add("content");
|
vbox.getStyleClass().add("content");
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,11 +8,11 @@ import io.xpipe.app.storage.DataStoreEntry;
|
||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
import javafx.beans.value.ObservableBooleanValue;
|
import javafx.beans.value.ObservableBooleanValue;
|
||||||
|
import javafx.beans.value.ObservableValue;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import lombok.Value;
|
import lombok.Value;
|
||||||
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
|
||||||
@Value
|
@Value
|
||||||
|
@ -32,6 +32,12 @@ public class StoreSection implements StorageFilter.Filterable {
|
||||||
int depth;
|
int depth;
|
||||||
ObservableBooleanValue showDetails;
|
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) {
|
public StoreSection(StoreEntryWrapper wrapper, ObservableList<StoreSection> children, int depth) {
|
||||||
this.wrapper = wrapper;
|
this.wrapper = wrapper;
|
||||||
this.children = children;
|
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() {
|
public static StoreSection createTopLevel() {
|
||||||
var topLevel = BindingsHelper.cachedMappedContentBinding(
|
var topLevel = BindingsHelper.cachedMappedContentBinding(
|
||||||
StoreViewState.get().getAllEntries(), storeEntryWrapper -> create(storeEntryWrapper, 1));
|
StoreViewState.get().getAllEntries(), storeEntryWrapper -> create(storeEntryWrapper, 1));
|
||||||
|
@ -64,7 +62,7 @@ public class StoreSection implements StorageFilter.Filterable {
|
||||||
.getParent(section.getWrapper().getEntry(), true)
|
.getParent(section.getWrapper().getEntry(), true)
|
||||||
.isEmpty();
|
.isEmpty();
|
||||||
});
|
});
|
||||||
var ordered = BindingsHelper.orderedContentBinding(filtered, COMPARATOR);
|
var ordered = BindingsHelper.orderedContentBinding(filtered, sortMode);
|
||||||
return new StoreSection(null, ordered, 0);
|
return new StoreSection(null, ordered, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +79,7 @@ public class StoreSection implements StorageFilter.Filterable {
|
||||||
.orElse(false);
|
.orElse(false);
|
||||||
});
|
});
|
||||||
var children = BindingsHelper.cachedMappedContentBinding(filtered, entry1 -> create(entry1, depth + 1));
|
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);
|
return new StoreSection(e, ordered, depth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,13 +10,15 @@ import javafx.scene.layout.VBox;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class StoreSidebarComp extends SimpleComp {
|
public class StoreSidebarComp extends SimpleComp {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Region createSimple() {
|
protected Region createSimple() {
|
||||||
var sideBar = new VerticalComp(List.of(
|
var sideBar = new VerticalComp(List.of(
|
||||||
new StoreEntryListHeaderComp(),
|
new StoreEntryListHeaderComp(),
|
||||||
new StoreCreationBarComp(),
|
new StoreCreationBarComp(),
|
||||||
|
new StoreOrganizationComp(),
|
||||||
Comp.of(() -> new Region()).styleClass("bar").styleClass("filler-bar")));
|
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");
|
sideBar.styleClass("sidebar");
|
||||||
return sideBar.createRegion();
|
return sideBar.createRegion();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
|
@ -1,15 +1,18 @@
|
||||||
package io.xpipe.app.comp.storage.store;
|
package io.xpipe.app.comp.storage.store;
|
||||||
|
|
||||||
import io.xpipe.app.comp.storage.StorageFilter;
|
import io.xpipe.app.comp.storage.StorageFilter;
|
||||||
|
import io.xpipe.app.core.AppCache;
|
||||||
import io.xpipe.app.issue.ErrorEvent;
|
import io.xpipe.app.issue.ErrorEvent;
|
||||||
import io.xpipe.app.storage.DataStorage;
|
import io.xpipe.app.storage.DataStorage;
|
||||||
import io.xpipe.app.storage.DataStoreEntry;
|
import io.xpipe.app.storage.DataStoreEntry;
|
||||||
import io.xpipe.app.storage.StorageListener;
|
import io.xpipe.app.storage.StorageListener;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
|
import javafx.beans.property.Property;
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
import javafx.beans.value.ObservableValue;
|
import javafx.beans.value.ObservableValue;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -28,7 +31,17 @@ public class StoreViewState {
|
||||||
private final ObservableList<StoreEntryWrapper> shownEntries =
|
private final ObservableList<StoreEntryWrapper> shownEntries =
|
||||||
FXCollections.observableList(new CopyOnWriteArrayList<>());
|
FXCollections.observableList(new CopyOnWriteArrayList<>());
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private final Property<StoreSortMode> sortMode;
|
||||||
|
|
||||||
|
|
||||||
private StoreViewState() {
|
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 {
|
try {
|
||||||
addStorageGroupListeners();
|
addStorageGroupListeners();
|
||||||
addShownContentChangeListeners();
|
addShownContentChangeListeners();
|
||||||
|
|
|
@ -142,15 +142,18 @@ public class BindingsHelper {
|
||||||
return l1;
|
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<>());
|
ObservableList<V> l1 = FXCollections.observableList(new ArrayList<>());
|
||||||
Runnable runnable = () -> {
|
Runnable runnable = () -> {
|
||||||
setContent(l1, l2.stream().sorted(comp).toList());
|
setContent(l1, l2.stream().sorted(comp.getValue()).toList());
|
||||||
};
|
};
|
||||||
runnable.run();
|
runnable.run();
|
||||||
l2.addListener((ListChangeListener<? super V>) c -> {
|
l2.addListener((ListChangeListener<? super V>) c -> {
|
||||||
runnable.run();
|
runnable.run();
|
||||||
});
|
});
|
||||||
|
comp.addListener((observable, oldValue, newValue) -> {
|
||||||
|
runnable.run();
|
||||||
|
});
|
||||||
linkPersistently(l2, l1);
|
linkPersistently(l2, l1);
|
||||||
return l1;
|
return l1;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue