mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-25 00:50:31 +00:00
Rework bindings
This commit is contained in:
parent
4d04fbd874
commit
cd2933eef8
43 changed files with 340 additions and 435 deletions
|
@ -8,10 +8,9 @@ import io.xpipe.app.comp.base.SideSplitPaneComp;
|
|||
import io.xpipe.app.core.AppLayoutModel;
|
||||
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.PrettyImageHelper;
|
||||
import io.xpipe.app.fxcomps.impl.TooltipAugment;
|
||||
import io.xpipe.app.fxcomps.impl.VerticalComp;
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.util.BooleanScope;
|
||||
|
@ -122,7 +121,7 @@ public class BrowserComp extends SimpleComp {
|
|||
private Comp<?> createTabs() {
|
||||
var multi = new MultiContentComp(Map.<Comp<?>, ObservableValue<Boolean>>of(
|
||||
Comp.of(() -> createTabPane()),
|
||||
BindingsHelper.persist(Bindings.isNotEmpty(model.getOpenFileSystems())),
|
||||
Bindings.isNotEmpty(model.getOpenFileSystems()),
|
||||
new BrowserWelcomeComp(model).apply(struc -> StackPane.setAlignment(struc.get(), Pos.CENTER_LEFT)),
|
||||
Bindings.createBooleanBinding(
|
||||
() -> {
|
||||
|
@ -292,7 +291,7 @@ public class BrowserComp extends SimpleComp {
|
|||
if (color != null) {
|
||||
c.getStyleClass().add(color.getId());
|
||||
}
|
||||
new FancyTooltipAugment<>(new SimpleStringProperty(model.getTooltip())).augment(c);
|
||||
new TooltipAugment<>(new SimpleStringProperty(model.getTooltip())).augment(c);
|
||||
c.addEventHandler(
|
||||
DragEvent.DRAG_ENTERED,
|
||||
mouseEvent -> Platform.runLater(
|
||||
|
|
|
@ -9,7 +9,6 @@ import io.xpipe.app.fxcomps.SimpleComp;
|
|||
import io.xpipe.app.fxcomps.SimpleCompStructure;
|
||||
import io.xpipe.app.fxcomps.augment.ContextMenuAugment;
|
||||
import io.xpipe.app.fxcomps.impl.PrettyImageHelper;
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
import io.xpipe.app.util.BooleanScope;
|
||||
import io.xpipe.app.util.HumanReadableFormat;
|
||||
|
@ -505,7 +504,7 @@ final class BrowserFileListComp extends SimpleComp {
|
|||
.get();
|
||||
var quickAccess = new BrowserQuickAccessButtonComp(
|
||||
() -> getTableRow().getItem(), fileList.getFileSystemModel())
|
||||
.hide(BindingsHelper.persist(Bindings.createBooleanBinding(
|
||||
.hide(Bindings.createBooleanBinding(
|
||||
() -> {
|
||||
var item = getTableRow().getItem();
|
||||
var notDir = item.getRawFileEntry().resolved().getKind() != FileKind.DIRECTORY;
|
||||
|
@ -514,7 +513,7 @@ final class BrowserFileListComp extends SimpleComp {
|
|||
.equals(fileList.getFileSystemModel().getCurrentParentDirectory());
|
||||
return notDir || isParentLink;
|
||||
},
|
||||
itemProperty())))
|
||||
itemProperty()).not().not())
|
||||
.createRegion();
|
||||
|
||||
editing.addListener((observable, oldValue, newValue) -> {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package io.xpipe.app.browser;
|
||||
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.fxcomps.util.ListBindingsHelper;
|
||||
import io.xpipe.app.issue.ErrorEvent;
|
||||
import io.xpipe.core.store.FileKind;
|
||||
import io.xpipe.core.store.FileNames;
|
||||
|
@ -33,7 +33,7 @@ public final class BrowserFileListModel {
|
|||
private final ObservableList<BrowserEntry> previousSelection = FXCollections.observableArrayList();
|
||||
private final ObservableList<BrowserEntry> selection = FXCollections.observableArrayList();
|
||||
private final ObservableList<FileSystem.FileEntry> selectedRaw =
|
||||
BindingsHelper.mappedContentBinding(selection, entry -> entry.getRawFileEntry());
|
||||
ListBindingsHelper.mappedContentBinding(selection, entry -> entry.getRawFileEntry());
|
||||
|
||||
private final Property<BrowserEntry> draggedOverDirectory = new SimpleObjectProperty<>();
|
||||
private final Property<Boolean> draggedOverEmpty = new SimpleBooleanProperty();
|
||||
|
|
|
@ -3,7 +3,7 @@ package io.xpipe.app.browser;
|
|||
import atlantafx.base.theme.Styles;
|
||||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.fxcomps.CompStructure;
|
||||
import io.xpipe.app.fxcomps.impl.FancyTooltipAugment;
|
||||
import io.xpipe.app.fxcomps.impl.TooltipAugment;
|
||||
import io.xpipe.app.fxcomps.impl.TextFieldComp;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
|
@ -28,7 +28,7 @@ public class BrowserFilterComp extends Comp<BrowserFilterComp.Structure> {
|
|||
var expanded = new SimpleBooleanProperty();
|
||||
var text = new TextFieldComp(filterString, false).createRegion();
|
||||
var button = new Button();
|
||||
new FancyTooltipAugment<>("app.search").augment(button);
|
||||
new TooltipAugment<>("app.search").augment(button);
|
||||
text.focusedProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if (!newValue && filterString.getValue() == null) {
|
||||
if (button.isFocused()) {
|
||||
|
|
|
@ -4,6 +4,7 @@ import atlantafx.base.theme.Styles;
|
|||
import io.xpipe.app.core.AppFont;
|
||||
import io.xpipe.app.core.AppLayoutModel;
|
||||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.Region;
|
||||
|
||||
|
@ -15,7 +16,9 @@ public class BrowserGreetingComp extends SimpleComp {
|
|||
protected Region createSimple() {
|
||||
var r = new Label(getText());
|
||||
AppLayoutModel.get().getSelected().addListener((observableValue, entry, t1) -> {
|
||||
r.setText(getText());
|
||||
PlatformThread.runLaterIfNeeded(() -> {
|
||||
r.setText(getText());
|
||||
});
|
||||
});
|
||||
AppFont.setSize(r, 7);
|
||||
r.getStyleClass().add(Styles.TEXT_BOLD);
|
||||
|
|
|
@ -3,7 +3,7 @@ package io.xpipe.app.browser;
|
|||
import io.xpipe.app.browser.icon.BrowserIconDirectoryType;
|
||||
import io.xpipe.app.browser.icon.BrowserIconFileType;
|
||||
import io.xpipe.app.browser.icon.FileIconManager;
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.fxcomps.util.ListBindingsHelper;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStoreEntryRef;
|
||||
import io.xpipe.app.util.BooleanScope;
|
||||
|
@ -50,7 +50,7 @@ public class BrowserModel {
|
|||
return;
|
||||
}
|
||||
|
||||
BindingsHelper.bindContent(selection, newValue.getFileList().getSelection());
|
||||
ListBindingsHelper.bindContent(selection, newValue.getFileList().getSelection());
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import io.xpipe.app.comp.base.SimpleTitledPaneComp;
|
|||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
import io.xpipe.app.fxcomps.impl.VerticalComp;
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.fxcomps.util.ListBindingsHelper;
|
||||
import io.xpipe.app.issue.ErrorEvent;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import io.xpipe.core.process.ShellControl;
|
||||
|
@ -66,7 +66,7 @@ public class BrowserOverviewComp extends SimpleComp {
|
|||
var rootsOverview = new BrowserFileOverviewComp(model, FXCollections.observableArrayList(roots), false);
|
||||
var rootsPane = new SimpleTitledPaneComp(AppI18n.observable("roots"), rootsOverview);
|
||||
|
||||
var recent = BindingsHelper.mappedContentBinding(
|
||||
var recent = ListBindingsHelper.mappedContentBinding(
|
||||
model.getSavedState().getRecentDirectories(),
|
||||
s -> FileSystem.FileEntry.ofDirectory(model.getFileSystem(), s.getDirectory()));
|
||||
var recentOverview = new BrowserFileOverviewComp(model, recent, true);
|
||||
|
|
|
@ -52,9 +52,9 @@ public class BrowserSelectionListComp extends SimpleComp {
|
|||
protected Region createSimple() {
|
||||
var c = new ListBoxViewComp<>(list, list, entry -> {
|
||||
return Comp.of(() -> {
|
||||
var wv = PrettyImageHelper.ofFixedSizeSquare(FileIconManager.getFileIcon(entry, false), 20)
|
||||
var image = PrettyImageHelper.ofFixedSizeSquare(FileIconManager.getFileIcon(entry, false), 24)
|
||||
.createRegion();
|
||||
var l = new Label(null, wv);
|
||||
var l = new Label(null, image);
|
||||
l.setTextOverrun(OverrunStyle.CENTER_ELLIPSIS);
|
||||
l.textProperty().bind(PlatformThread.sync(nameTransformation.apply(entry)));
|
||||
return l;
|
||||
|
|
|
@ -59,7 +59,7 @@ public class BrowserStatusBarComp extends SimpleComp {
|
|||
|
||||
private Comp<?> createClipboardStatus() {
|
||||
var cc = BrowserClipboard.currentCopyClipboard;
|
||||
var ccCount = (BindingsHelper.persist(Bindings.createStringBinding(
|
||||
var ccCount = Bindings.createStringBinding(
|
||||
() -> {
|
||||
if (cc.getValue() != null && cc.getValue().getEntries().size() > 0) {
|
||||
return cc.getValue().getEntries().size() + " file"
|
||||
|
@ -68,7 +68,7 @@ public class BrowserStatusBarComp extends SimpleComp {
|
|||
return null;
|
||||
}
|
||||
},
|
||||
cc)));
|
||||
cc);
|
||||
return new LabelComp(ccCount);
|
||||
}
|
||||
|
||||
|
@ -86,7 +86,7 @@ public class BrowserStatusBarComp extends SimpleComp {
|
|||
.count();
|
||||
},
|
||||
model.getFileList().getAll());
|
||||
var selectedComp = new LabelComp(BindingsHelper.persist(Bindings.createStringBinding(
|
||||
var selectedComp = new LabelComp(Bindings.createStringBinding(
|
||||
() -> {
|
||||
if (selectedCount.getValue().intValue() == 0) {
|
||||
return null;
|
||||
|
@ -95,7 +95,7 @@ public class BrowserStatusBarComp extends SimpleComp {
|
|||
}
|
||||
},
|
||||
selectedCount,
|
||||
allCount)));
|
||||
allCount));
|
||||
return selectedComp;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import io.xpipe.app.fxcomps.Comp;
|
|||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
import io.xpipe.app.fxcomps.augment.DragOverPseudoClassAugment;
|
||||
import io.xpipe.app.fxcomps.impl.*;
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.fxcomps.util.ListBindingsHelper;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.core.process.OsType;
|
||||
|
@ -39,11 +39,11 @@ public class BrowserTransferComp extends SimpleComp {
|
|||
protected Region createSimple() {
|
||||
var background = new LabelComp(AppI18n.observable("transferDescription"))
|
||||
.apply(struc -> struc.get().setGraphic(new FontIcon("mdi2d-download-outline")))
|
||||
.visible(BindingsHelper.persist(Bindings.isEmpty(model.getItems())));
|
||||
.visible(Bindings.isEmpty(model.getItems()));
|
||||
var backgroundStack =
|
||||
new StackComp(List.of(background)).grow(true, true).styleClass("download-background");
|
||||
|
||||
var binding = BindingsHelper.mappedContentBinding(model.getItems(), item -> item.getFileEntry());
|
||||
var binding = ListBindingsHelper.mappedContentBinding(model.getItems(), item -> item.getFileEntry());
|
||||
var list = new BrowserSelectionListComp(
|
||||
binding,
|
||||
entry -> Bindings.createStringBinding(
|
||||
|
@ -70,20 +70,20 @@ public class BrowserTransferComp extends SimpleComp {
|
|||
.flatMap(aBoolean ->
|
||||
aBoolean ? AppI18n.observable("dragLocalFiles") : AppI18n.observable("dragFiles")))
|
||||
.apply(struc -> struc.get().setGraphic(new FontIcon("mdi2h-hand-left")))
|
||||
.hide(PlatformThread.sync(BindingsHelper.persist(Bindings.isEmpty(model.getItems()))))
|
||||
.hide(PlatformThread.sync(Bindings.isEmpty(model.getItems())))
|
||||
.grow(true, false)
|
||||
.apply(struc -> struc.get().setPadding(new Insets(8)));
|
||||
|
||||
var downloadButton = new IconButtonComp("mdi2d-download", () -> {
|
||||
model.download();
|
||||
})
|
||||
.hide(BindingsHelper.persist(Bindings.isEmpty(model.getItems())))
|
||||
.hide(Bindings.isEmpty(model.getItems()))
|
||||
.disable(PlatformThread.sync(model.getAllDownloaded()))
|
||||
.apply(new FancyTooltipAugment<>("downloadStageDescription"));
|
||||
.apply(new TooltipAugment<>("downloadStageDescription"));
|
||||
var clearButton = new IconButtonComp("mdi2c-close", () -> {
|
||||
model.clear();
|
||||
})
|
||||
.hide(BindingsHelper.persist(Bindings.isEmpty(model.getItems())));
|
||||
.hide(Bindings.isEmpty(model.getItems()));
|
||||
var clearPane = Comp.derive(
|
||||
new HorizontalComp(List.of(downloadButton, clearButton))
|
||||
.apply(struc -> struc.get().setSpacing(10)),
|
||||
|
|
|
@ -13,6 +13,7 @@ import io.xpipe.app.fxcomps.impl.LabelComp;
|
|||
import io.xpipe.app.fxcomps.impl.PrettyImageHelper;
|
||||
import io.xpipe.app.fxcomps.impl.PrettySvgComp;
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.fxcomps.util.ListBindingsHelper;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import javafx.beans.binding.Bindings;
|
||||
|
@ -62,7 +63,7 @@ public class BrowserWelcomeComp extends SimpleComp {
|
|||
return new VBox(hbox);
|
||||
}
|
||||
|
||||
var list = BindingsHelper.filteredContentBinding(state.getEntries(), e -> {
|
||||
var list = ListBindingsHelper.filteredContentBinding(state.getEntries(), e -> {
|
||||
var entry = DataStorage.get().getStoreEntryIfPresent(e.getUuid());
|
||||
if (entry.isEmpty()) {
|
||||
return false;
|
||||
|
@ -76,7 +77,7 @@ public class BrowserWelcomeComp extends SimpleComp {
|
|||
});
|
||||
var empty = Bindings.createBooleanBinding(() -> list.isEmpty(), list);
|
||||
|
||||
var headerBinding = BindingsHelper.mappedBinding(empty,b -> {
|
||||
var headerBinding = BindingsHelper.flatMap(empty,b -> {
|
||||
if (b) {
|
||||
return AppI18n.observable("browserWelcomeEmpty");
|
||||
} else {
|
||||
|
|
|
@ -9,7 +9,6 @@ import io.xpipe.app.fxcomps.SimpleComp;
|
|||
import io.xpipe.app.fxcomps.SimpleCompStructure;
|
||||
import io.xpipe.app.fxcomps.augment.ContextMenuAugment;
|
||||
import io.xpipe.app.fxcomps.impl.VerticalComp;
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.fxcomps.util.Shortcuts;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
|
@ -97,7 +96,7 @@ public class OpenFileSystemComp extends SimpleComp {
|
|||
home,
|
||||
model.getCurrentPath().isNull(),
|
||||
fileList,
|
||||
BindingsHelper.persist(model.getCurrentPath().isNull().not())));
|
||||
model.getCurrentPath().isNull().not()));
|
||||
return stack.createRegion();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ package io.xpipe.app.browser.action;
|
|||
|
||||
import io.xpipe.app.browser.BrowserEntry;
|
||||
import io.xpipe.app.browser.OpenFileSystemModel;
|
||||
import io.xpipe.app.fxcomps.impl.FancyTooltipAugment;
|
||||
import io.xpipe.app.fxcomps.impl.TooltipAugment;
|
||||
import io.xpipe.app.fxcomps.util.Shortcuts;
|
||||
import io.xpipe.app.util.BooleanScope;
|
||||
import io.xpipe.app.util.LicenseProvider;
|
||||
|
@ -39,7 +39,7 @@ public interface LeafAction extends BrowserAction {
|
|||
if (getShortcut() != null) {
|
||||
Shortcuts.addShortcut(b, getShortcut());
|
||||
}
|
||||
new FancyTooltipAugment<>(new SimpleStringProperty(getName(model, selected))).augment(b);
|
||||
new TooltipAugment<>(new SimpleStringProperty(getName(model, selected))).augment(b);
|
||||
var graphic = getIcon(model, selected);
|
||||
if (graphic != null) {
|
||||
b.setGraphic(graphic);
|
||||
|
|
|
@ -7,7 +7,6 @@ import io.xpipe.app.core.AppLayoutModel;
|
|||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.fxcomps.CompStructure;
|
||||
import io.xpipe.app.fxcomps.SimpleCompStructure;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import javafx.beans.binding.Bindings;
|
||||
|
@ -25,14 +24,15 @@ public class AppLayoutComp extends Comp<CompStructure<Pane>> {
|
|||
var multi = new MultiContentComp(model.getEntries().stream()
|
||||
.collect(Collectors.toMap(
|
||||
entry -> entry.comp(),
|
||||
entry -> PlatformThread.sync(Bindings.createBooleanBinding(
|
||||
entry -> Bindings.createBooleanBinding(
|
||||
() -> {
|
||||
return model.getSelected().getValue().equals(entry);
|
||||
},
|
||||
model.getSelected())))));
|
||||
model.getSelected())))
|
||||
);
|
||||
|
||||
var pane = new BorderPane();
|
||||
var sidebar = new SideMenuBarComp(model.getSelectedInternal(), model.getEntries());
|
||||
var sidebar = new SideMenuBarComp(model.getSelected(), model.getEntries());
|
||||
pane.setCenter(multi.createRegion());
|
||||
pane.setRight(sidebar.createRegion());
|
||||
pane.getStyleClass().add("background");
|
||||
|
|
|
@ -4,7 +4,7 @@ import io.xpipe.app.fxcomps.Comp;
|
|||
import io.xpipe.app.fxcomps.CompStructure;
|
||||
import io.xpipe.app.fxcomps.SimpleCompStructure;
|
||||
import io.xpipe.app.fxcomps.augment.ContextMenuAugment;
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.fxcomps.util.ListBindingsHelper;
|
||||
import javafx.css.Size;
|
||||
import javafx.css.SizeUnits;
|
||||
import javafx.scene.control.Button;
|
||||
|
@ -37,7 +37,7 @@ public class DropdownComp extends Comp<CompStructure<Button>> {
|
|||
.createRegion();
|
||||
|
||||
button.visibleProperty()
|
||||
.bind(BindingsHelper.anyMatch(cm.getItems().stream()
|
||||
.bind(ListBindingsHelper.anyMatch(cm.getItems().stream()
|
||||
.map(menuItem -> menuItem.getGraphic().visibleProperty())
|
||||
.toList()));
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ package io.xpipe.app.comp.base;
|
|||
|
||||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.fxcomps.CompStructure;
|
||||
import io.xpipe.app.fxcomps.SimpleCompStructure;
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
|
@ -37,9 +36,10 @@ public class FontIconComp extends Comp <FontIconComp.Structure>{
|
|||
public FontIconComp.Structure createBase() {
|
||||
var fi = new FontIcon();
|
||||
var obs = PlatformThread.sync(icon);
|
||||
BindingsHelper.linkPersistently(fi, obs);
|
||||
obs.subscribe(val -> {
|
||||
fi.setIconLiteral(val);
|
||||
icon.subscribe(val -> {
|
||||
PlatformThread.runLaterIfNeeded(() -> {
|
||||
fi.setIconLiteral(val);
|
||||
});
|
||||
});
|
||||
|
||||
var pane = new StackPane(fi);
|
||||
|
|
|
@ -3,7 +3,7 @@ package io.xpipe.app.comp.base;
|
|||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.fxcomps.CompStructure;
|
||||
import io.xpipe.app.fxcomps.SimpleCompStructure;
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.fxcomps.util.ListBindingsHelper;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.ListChangeListener;
|
||||
|
@ -88,7 +88,7 @@ public class ListBoxViewComp<T> extends Comp<CompStructure<ScrollPane>> {
|
|||
}
|
||||
|
||||
if (!listView.getChildren().equals(newShown)) {
|
||||
BindingsHelper.setContent(listView.getChildren(), newShown);
|
||||
ListBindingsHelper.setContent(listView.getChildren(), newShown);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -24,9 +24,11 @@ public class MultiContentComp extends SimpleComp {
|
|||
for (Map.Entry<Comp<?>, ObservableValue<Boolean>> entry : content.entrySet()) {
|
||||
var region = entry.getKey().createRegion();
|
||||
stack.getChildren().add(region);
|
||||
PlatformThread.sync(entry.getValue()).subscribe(val -> {
|
||||
region.setManaged(val);
|
||||
region.setVisible(val);
|
||||
entry.getValue().subscribe(val -> {
|
||||
PlatformThread.runLaterIfNeeded(() -> {
|
||||
region.setManaged(val);
|
||||
region.setVisible(val);
|
||||
});
|
||||
});
|
||||
}
|
||||
return stack;
|
||||
|
|
|
@ -37,7 +37,7 @@ public class OsLogoComp extends SimpleComp {
|
|||
|
||||
@Override
|
||||
protected Region createSimple() {
|
||||
var img = BindingsHelper.persist(Bindings.createObjectBinding(
|
||||
var img = Bindings.createObjectBinding(
|
||||
() -> {
|
||||
if (state.getValue() != SystemStateComp.State.SUCCESS) {
|
||||
return null;
|
||||
|
@ -51,7 +51,7 @@ public class OsLogoComp extends SimpleComp {
|
|||
return getImage(ons.getOsName());
|
||||
},
|
||||
wrapper.getPersistentState(),
|
||||
state));
|
||||
state);
|
||||
var hide = BindingsHelper.map(img, s -> s != null);
|
||||
return new StackComp(
|
||||
List.of(new SystemStateComp(state).hide(hide), new PrettyImageComp(img, 24, 24).visible(hide)))
|
||||
|
|
|
@ -7,7 +7,7 @@ import io.xpipe.app.fxcomps.Comp;
|
|||
import io.xpipe.app.fxcomps.CompStructure;
|
||||
import io.xpipe.app.fxcomps.SimpleCompStructure;
|
||||
import io.xpipe.app.fxcomps.augment.Augment;
|
||||
import io.xpipe.app.fxcomps.impl.FancyTooltipAugment;
|
||||
import io.xpipe.app.fxcomps.impl.TooltipAugment;
|
||||
import io.xpipe.app.fxcomps.impl.IconButtonComp;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
import io.xpipe.app.issue.ErrorEvent;
|
||||
|
@ -73,7 +73,7 @@ public class SideMenuBarComp extends Comp<CompStructure<VBox>> {
|
|||
var e = entries.get(i);
|
||||
var b = new IconButtonComp(e.icon(), () -> value.setValue(e));
|
||||
b.shortcut(new KeyCodeCombination(KeyCode.values()[KeyCode.DIGIT1.ordinal() + i]));
|
||||
b.apply(new FancyTooltipAugment<>(e.name()));
|
||||
b.apply(new TooltipAugment<>(e.name()));
|
||||
b.apply(struc -> {
|
||||
AppFont.setSize(struc.get(), 2);
|
||||
struc.get().pseudoClassStateChanged(selected, value.getValue().equals(e));
|
||||
|
@ -133,7 +133,7 @@ public class SideMenuBarComp extends Comp<CompStructure<VBox>> {
|
|||
UserReportComp.show(event.build());
|
||||
})
|
||||
.shortcut(new KeyCodeCombination(KeyCode.values()[KeyCode.DIGIT1.ordinal() + entries.size()]))
|
||||
.apply(new FancyTooltipAugment<>("reportIssue"))
|
||||
.apply(new TooltipAugment<>("reportIssue"))
|
||||
.apply(simpleBorders)
|
||||
.accessibleTextKey("reportIssue");
|
||||
b.apply(struc -> {
|
||||
|
@ -145,7 +145,7 @@ public class SideMenuBarComp extends Comp<CompStructure<VBox>> {
|
|||
{
|
||||
var b = new IconButtonComp("mdi2g-github", () -> Hyperlinks.open(Hyperlinks.GITHUB))
|
||||
.shortcut(new KeyCodeCombination(KeyCode.values()[KeyCode.DIGIT1.ordinal() + entries.size() + 1]))
|
||||
.apply(new FancyTooltipAugment<>("visitGithubRepository"))
|
||||
.apply(new TooltipAugment<>("visitGithubRepository"))
|
||||
.apply(simpleBorders)
|
||||
.accessibleTextKey("visitGithubRepository");
|
||||
b.apply(struc -> {
|
||||
|
@ -157,7 +157,7 @@ public class SideMenuBarComp extends Comp<CompStructure<VBox>> {
|
|||
{
|
||||
var b = new IconButtonComp("mdi2d-discord", () -> Hyperlinks.open(Hyperlinks.DISCORD))
|
||||
.shortcut(new KeyCodeCombination(KeyCode.values()[KeyCode.DIGIT1.ordinal() + entries.size() + 2]))
|
||||
.apply(new FancyTooltipAugment<>("discord"))
|
||||
.apply(new TooltipAugment<>("discord"))
|
||||
.apply(simpleBorders)
|
||||
.accessibleTextKey("discord");
|
||||
b.apply(struc -> {
|
||||
|
@ -169,7 +169,7 @@ public class SideMenuBarComp extends Comp<CompStructure<VBox>> {
|
|||
{
|
||||
var b = new IconButtonComp("mdi2t-translate", () -> Hyperlinks.open(Hyperlinks.TRANSLATE))
|
||||
.shortcut(new KeyCodeCombination(KeyCode.values()[KeyCode.DIGIT1.ordinal() + entries.size() + 3]))
|
||||
.apply(new FancyTooltipAugment<>("translate"))
|
||||
.apply(new TooltipAugment<>("translate"))
|
||||
.apply(simpleBorders)
|
||||
.accessibleTextKey("translate");
|
||||
b.apply(struc -> {
|
||||
|
@ -180,7 +180,7 @@ public class SideMenuBarComp extends Comp<CompStructure<VBox>> {
|
|||
|
||||
{
|
||||
var b = new IconButtonComp("mdi2u-update", () -> UpdateAvailableAlert.showIfNeeded())
|
||||
.apply(new FancyTooltipAugment<>("updateAvailableTooltip"))
|
||||
.apply(new TooltipAugment<>("updateAvailableTooltip"))
|
||||
.accessibleTextKey("updateAvailableTooltip");
|
||||
b.apply(struc -> {
|
||||
AppFont.setSize(struc.get(), 2);
|
||||
|
|
|
@ -3,7 +3,6 @@ package io.xpipe.app.comp.base;
|
|||
import io.xpipe.app.comp.store.StoreSection;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import javafx.beans.binding.Bindings;
|
||||
|
@ -32,13 +31,13 @@ public class StoreToggleComp extends SimpleComp {
|
|||
@Override
|
||||
protected Region createSimple() {
|
||||
var disable = section.getWrapper().getValidity().map(state -> state != DataStoreEntry.Validity.COMPLETE);
|
||||
var visible = BindingsHelper.persist(Bindings.createBooleanBinding(
|
||||
var visible = Bindings.createBooleanBinding(
|
||||
() -> {
|
||||
return section.getWrapper().getValidity().getValue() == DataStoreEntry.Validity.COMPLETE
|
||||
&& section.getShowDetails().get();
|
||||
},
|
||||
section.getWrapper().getValidity(),
|
||||
section.getShowDetails()));
|
||||
section.getShowDetails());
|
||||
var t = new ToggleSwitchComp(value, AppI18n.observable(nameKey))
|
||||
.visible(visible)
|
||||
.disable(disable);
|
||||
|
|
|
@ -4,7 +4,6 @@ import io.xpipe.app.core.AppFont;
|
|||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.fxcomps.CompStructure;
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
|
@ -47,9 +46,9 @@ public class TileButtonComp extends Comp<TileButtonComp.Structure> {
|
|||
});
|
||||
|
||||
var header = new Label();
|
||||
BindingsHelper.bindStrong(header.textProperty(), PlatformThread.sync(name));
|
||||
header.textProperty().bind(PlatformThread.sync(name));
|
||||
var desc = new Label();
|
||||
BindingsHelper.bindStrong(desc.textProperty(), PlatformThread.sync(description));
|
||||
desc.textProperty().bind(PlatformThread.sync(description));
|
||||
AppFont.small(desc);
|
||||
desc.setOpacity(0.65);
|
||||
var text = new VBox(header, desc);
|
||||
|
|
|
@ -13,7 +13,6 @@ import io.xpipe.app.fxcomps.SimpleCompStructure;
|
|||
import io.xpipe.app.fxcomps.augment.ContextMenuAugment;
|
||||
import io.xpipe.app.fxcomps.augment.GrowAugment;
|
||||
import io.xpipe.app.fxcomps.impl.*;
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
|
@ -100,8 +99,7 @@ public abstract class StoreEntryComp extends SimpleComp {
|
|||
|
||||
var loading = LoadingOverlayComp.noProgress(
|
||||
Comp.of(() -> button),
|
||||
BindingsHelper.persist(
|
||||
wrapper.getInRefresh().and(wrapper.getObserving().not())));
|
||||
wrapper.getInRefresh().and(wrapper.getObserving().not()));
|
||||
return loading.createRegion();
|
||||
}
|
||||
|
||||
|
@ -173,7 +171,7 @@ public abstract class StoreEntryComp extends SimpleComp {
|
|||
var imageComp = PrettyImageHelper.ofFixedSize(img, w, h);
|
||||
var storeIcon = imageComp.createRegion();
|
||||
if (wrapper.getValidity().getValue().isUsable()) {
|
||||
new FancyTooltipAugment<>(new SimpleStringProperty(
|
||||
new TooltipAugment<>(new SimpleStringProperty(
|
||||
wrapper.getEntry().getProvider().getDisplayName()))
|
||||
.augment(storeIcon);
|
||||
}
|
||||
|
@ -211,7 +209,7 @@ public abstract class StoreEntryComp extends SimpleComp {
|
|||
});
|
||||
button.accessibleText(
|
||||
actionProvider.getName(wrapper.getEntry().ref()).getValue());
|
||||
button.apply(new FancyTooltipAugment<>(
|
||||
button.apply(new TooltipAugment<>(
|
||||
actionProvider.getName(wrapper.getEntry().ref())));
|
||||
if (actionProvider.activeType() == ActionProvider.DataStoreCallSite.ActiveType.ONLY_SHOW_IF_ENABLED) {
|
||||
button.hide(Bindings.not(p.getValue()));
|
||||
|
@ -247,7 +245,7 @@ public abstract class StoreEntryComp extends SimpleComp {
|
|||
settingsButton.accessibleText("More");
|
||||
settingsButton.apply(new ContextMenuAugment<>(
|
||||
event -> event.getButton() == MouseButton.PRIMARY, null, () -> StoreEntryComp.this.createContextMenu()));
|
||||
settingsButton.apply(new FancyTooltipAugment<>("more"));
|
||||
settingsButton.apply(new TooltipAugment<>("more"));
|
||||
return settingsButton;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ import io.xpipe.app.comp.base.MultiContentComp;
|
|||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
import io.xpipe.app.fxcomps.impl.HorizontalComp;
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.geometry.Insets;
|
||||
|
@ -50,16 +49,16 @@ public class StoreEntryListComp extends SimpleComp {
|
|||
var map = new LinkedHashMap<Comp<?>, ObservableValue<Boolean>>();
|
||||
map.put(
|
||||
createList(),
|
||||
BindingsHelper.persist(Bindings.not(Bindings.isEmpty(
|
||||
StoreViewState.get().getCurrentTopLevelSection().getShownChildren()))));
|
||||
Bindings.not(Bindings.isEmpty(
|
||||
StoreViewState.get().getCurrentTopLevelSection().getShownChildren())));
|
||||
|
||||
map.put(new StoreIntroComp(), showIntro);
|
||||
map.put(
|
||||
new StoreNotFoundComp(),
|
||||
BindingsHelper.persist(Bindings.and(
|
||||
Bindings.and(
|
||||
Bindings.not(Bindings.isEmpty(StoreViewState.get().getAllEntries())),
|
||||
Bindings.isEmpty(
|
||||
StoreViewState.get().getCurrentTopLevelSection().getShownChildren()))));
|
||||
StoreViewState.get().getCurrentTopLevelSection().getShownChildren())));
|
||||
return new MultiContentComp(map).createRegion();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,10 +5,11 @@ import io.xpipe.app.core.AppFont;
|
|||
import io.xpipe.app.core.AppI18n;
|
||||
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.TooltipAugment;
|
||||
import io.xpipe.app.fxcomps.impl.FilterComp;
|
||||
import io.xpipe.app.fxcomps.impl.IconButtonComp;
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.fxcomps.util.ListBindingsHelper;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import io.xpipe.core.process.OsType;
|
||||
import javafx.beans.binding.Bindings;
|
||||
|
@ -55,7 +56,7 @@ public class StoreEntryListStatusComp extends SimpleComp {
|
|||
label.textProperty().bind(name);
|
||||
label.getStyleClass().add("name");
|
||||
|
||||
var all = BindingsHelper.filteredContentBinding(
|
||||
var all = ListBindingsHelper.filteredContentBinding(
|
||||
StoreViewState.get().getAllEntries(),
|
||||
storeEntryWrapper -> {
|
||||
var storeRoot = storeEntryWrapper.getCategory().getValue().getRoot();
|
||||
|
@ -66,7 +67,7 @@ public class StoreEntryListStatusComp extends SimpleComp {
|
|||
.equals(storeRoot);
|
||||
},
|
||||
StoreViewState.get().getActiveCategory());
|
||||
var shownList = BindingsHelper.filteredContentBinding(
|
||||
var shownList = ListBindingsHelper.filteredContentBinding(
|
||||
all,
|
||||
storeEntryWrapper -> {
|
||||
return storeEntryWrapper.shouldShow(
|
||||
|
@ -178,7 +179,7 @@ public class StoreEntryListStatusComp extends SimpleComp {
|
|||
sortMode));
|
||||
});
|
||||
alphabetical.accessibleTextKey("sortAlphabetical");
|
||||
alphabetical.apply(new FancyTooltipAugment<>("sortAlphabetical"));
|
||||
alphabetical.apply(new TooltipAugment<>("sortAlphabetical"));
|
||||
return alphabetical;
|
||||
}
|
||||
|
||||
|
@ -217,7 +218,7 @@ public class StoreEntryListStatusComp extends SimpleComp {
|
|||
sortMode));
|
||||
});
|
||||
date.accessibleTextKey("sortLastUsed");
|
||||
date.apply(new FancyTooltipAugment<>("sortLastUsed"));
|
||||
date.apply(new TooltipAugment<>("sortLastUsed"));
|
||||
return date;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package io.xpipe.app.comp.store;
|
|||
|
||||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.fxcomps.util.ListBindingsHelper;
|
||||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
|
@ -64,10 +65,10 @@ public class StoreSection {
|
|||
|
||||
var c = Comparator.<StoreSection>comparingInt(
|
||||
value -> value.getWrapper().getEntry().getValidity().isUsable() ? -1 : 1);
|
||||
var mappedSortMode = BindingsHelper.mappedBinding(
|
||||
var mappedSortMode = BindingsHelper.flatMap(
|
||||
category,
|
||||
storeCategoryWrapper -> storeCategoryWrapper != null ? storeCategoryWrapper.getSortMode() : null);
|
||||
return BindingsHelper.orderedContentBinding(
|
||||
return ListBindingsHelper.orderedContentBinding(
|
||||
list,
|
||||
(o1, o2) -> {
|
||||
var current = mappedSortMode.getValue();
|
||||
|
@ -86,16 +87,16 @@ public class StoreSection {
|
|||
Predicate<StoreEntryWrapper> entryFilter,
|
||||
ObservableStringValue filterString,
|
||||
ObservableValue<StoreCategoryWrapper> category) {
|
||||
var topLevel = BindingsHelper.filteredContentBinding(
|
||||
var topLevel = ListBindingsHelper.filteredContentBinding(
|
||||
all,
|
||||
section -> {
|
||||
return DataStorage.get().isRootEntry(section.getEntry());
|
||||
},
|
||||
category);
|
||||
var cached = BindingsHelper.cachedMappedContentBinding(
|
||||
topLevel, storeEntryWrapper -> create(storeEntryWrapper, 1, all, entryFilter, filterString, category));
|
||||
var cached = ListBindingsHelper.cachedMappedContentBinding(
|
||||
topLevel, topLevel, storeEntryWrapper -> create(storeEntryWrapper, 1, all, entryFilter, filterString, category));
|
||||
var ordered = sorted(cached, category);
|
||||
var shown = BindingsHelper.filteredContentBinding(
|
||||
var shown = ListBindingsHelper.filteredContentBinding(
|
||||
ordered,
|
||||
section -> {
|
||||
var showFilter = filterString == null || section.shouldShow(filterString.get());
|
||||
|
@ -121,7 +122,7 @@ public class StoreSection {
|
|||
return new StoreSection(e, FXCollections.observableArrayList(), FXCollections.observableArrayList(), depth);
|
||||
}
|
||||
|
||||
var allChildren = BindingsHelper.filteredContentBinding(all, other -> {
|
||||
var allChildren = ListBindingsHelper.filteredContentBinding(all, other -> {
|
||||
// Legacy implementation that does not use children caches. Use for testing
|
||||
// if (true) return DataStorage.get()
|
||||
// .getDisplayParent(other.getEntry())
|
||||
|
@ -131,10 +132,10 @@ public class StoreSection {
|
|||
// This check is fast as the children are cached in the storage
|
||||
return DataStorage.get().getStoreChildren(e.getEntry()).contains(other.getEntry());
|
||||
});
|
||||
var cached = BindingsHelper.cachedMappedContentBinding(
|
||||
allChildren, entry1 -> create(entry1, depth + 1, all, entryFilter, filterString, category));
|
||||
var cached = ListBindingsHelper.cachedMappedContentBinding(
|
||||
allChildren, allChildren, entry1 -> create(entry1, depth + 1, all, entryFilter, filterString, category));
|
||||
var ordered = sorted(cached, category);
|
||||
var filtered = BindingsHelper.filteredContentBinding(
|
||||
var filtered = ListBindingsHelper.filteredContentBinding(
|
||||
ordered,
|
||||
section -> {
|
||||
var showFilter = filterString == null || section.shouldShow(filterString.get());
|
||||
|
|
|
@ -7,7 +7,7 @@ import io.xpipe.app.fxcomps.augment.GrowAugment;
|
|||
import io.xpipe.app.fxcomps.impl.HorizontalComp;
|
||||
import io.xpipe.app.fxcomps.impl.IconButtonComp;
|
||||
import io.xpipe.app.fxcomps.impl.VerticalComp;
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.fxcomps.util.ListBindingsHelper;
|
||||
import io.xpipe.app.storage.DataStoreColor;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import javafx.beans.binding.Bindings;
|
||||
|
@ -39,11 +39,11 @@ public class StoreSectionComp extends Comp<CompStructure<VBox>> {
|
|||
}
|
||||
|
||||
private Comp<CompStructure<Button>> createQuickAccessButton() {
|
||||
var quickAccessDisabled = BindingsHelper.persist(Bindings.createBooleanBinding(
|
||||
var quickAccessDisabled = Bindings.createBooleanBinding(
|
||||
() -> {
|
||||
return section.getShownChildren().isEmpty();
|
||||
},
|
||||
section.getShownChildren()));
|
||||
section.getShownChildren());
|
||||
Consumer<StoreEntryWrapper> quickAccessAction = w -> {
|
||||
ThreadHelper.runFailableAsync(() -> {
|
||||
w.executeDefaultAction();
|
||||
|
@ -90,8 +90,7 @@ public class StoreSectionComp extends Comp<CompStructure<VBox>> {
|
|||
return "Expand " + section.getWrapper().getName().getValue();
|
||||
},
|
||||
section.getWrapper().getName()))
|
||||
.disable(BindingsHelper.persist(
|
||||
Bindings.size(section.getShownChildren()).isEqualTo(0)))
|
||||
.disable(Bindings.size(section.getShownChildren()).isEqualTo(0))
|
||||
.styleClass("expand-button")
|
||||
.maxHeight(100)
|
||||
.vgrow();
|
||||
|
@ -130,7 +129,7 @@ public class StoreSectionComp extends Comp<CompStructure<VBox>> {
|
|||
|
||||
// Optimization for large sections. If there are more than 20 children, only add the nodes to the scene if the
|
||||
// section is actually expanded
|
||||
var listSections = BindingsHelper.filteredContentBinding(
|
||||
var listSections = ListBindingsHelper.filteredContentBinding(
|
||||
section.getShownChildren(),
|
||||
storeSection -> section.getAllChildren().size() <= 20
|
||||
|| section.getWrapper().getExpanded().get(),
|
||||
|
@ -142,22 +141,22 @@ public class StoreSectionComp extends Comp<CompStructure<VBox>> {
|
|||
.minHeight(0)
|
||||
.hgrow();
|
||||
|
||||
var expanded = BindingsHelper.persist(Bindings.createBooleanBinding(
|
||||
var expanded = Bindings.createBooleanBinding(
|
||||
() -> {
|
||||
return section.getWrapper().getExpanded().get()
|
||||
&& section.getShownChildren().size() > 0;
|
||||
},
|
||||
section.getWrapper().getExpanded(),
|
||||
section.getShownChildren()));
|
||||
section.getShownChildren());
|
||||
var full = new VerticalComp(List.of(
|
||||
topEntryList,
|
||||
Comp.separator().hide(BindingsHelper.persist(expanded.not())),
|
||||
Comp.separator().hide(expanded.not()),
|
||||
new HorizontalComp(List.of(content))
|
||||
.styleClass("content")
|
||||
.apply(struc -> struc.get().setFillHeight(true))
|
||||
.hide(BindingsHelper.persist(Bindings.or(
|
||||
.hide(Bindings.or(
|
||||
Bindings.not(section.getWrapper().getExpanded()),
|
||||
Bindings.size(section.getShownChildren()).isEqualTo(0))))));
|
||||
Bindings.size(section.getShownChildren()).isEqualTo(0)))));
|
||||
return full.styleClass("store-entry-section-comp")
|
||||
.apply(struc -> {
|
||||
struc.get().setFillWidth(true);
|
||||
|
|
|
@ -8,7 +8,7 @@ import io.xpipe.app.fxcomps.impl.HorizontalComp;
|
|||
import io.xpipe.app.fxcomps.impl.IconButtonComp;
|
||||
import io.xpipe.app.fxcomps.impl.PrettyImageHelper;
|
||||
import io.xpipe.app.fxcomps.impl.VerticalComp;
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.fxcomps.util.ListBindingsHelper;
|
||||
import io.xpipe.app.storage.DataStoreColor;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
|
@ -100,16 +100,15 @@ public class StoreSectionMiniComp extends Comp<CompStructure<VBox>> {
|
|||
+ section.getWrapper().getName().getValue();
|
||||
},
|
||||
section.getWrapper().getName()))
|
||||
.disable(BindingsHelper.persist(
|
||||
Bindings.size(section.getAllChildren()).isEqualTo(0)))
|
||||
.disable(Bindings.size(section.getAllChildren()).isEqualTo(0))
|
||||
.grow(false, true)
|
||||
.styleClass("expand-button");
|
||||
|
||||
var quickAccessDisabled = BindingsHelper.persist(Bindings.createBooleanBinding(
|
||||
var quickAccessDisabled = Bindings.createBooleanBinding(
|
||||
() -> {
|
||||
return section.getShownChildren().isEmpty();
|
||||
},
|
||||
section.getShownChildren()));
|
||||
section.getShownChildren());
|
||||
Consumer<StoreEntryWrapper> quickAccessAction = w -> {
|
||||
action.accept(w);
|
||||
};
|
||||
|
@ -133,7 +132,7 @@ public class StoreSectionMiniComp extends Comp<CompStructure<VBox>> {
|
|||
// Optimization for large sections. If there are more than 20 children, only add the nodes to the scene if the
|
||||
// section is actually expanded
|
||||
var listSections = section.getWrapper() != null
|
||||
? BindingsHelper.filteredContentBinding(
|
||||
? ListBindingsHelper.filteredContentBinding(
|
||||
section.getShownChildren(),
|
||||
storeSection -> section.getAllChildren().size() <= 20 || expanded.get(),
|
||||
expanded,
|
||||
|
@ -148,9 +147,9 @@ public class StoreSectionMiniComp extends Comp<CompStructure<VBox>> {
|
|||
list.add(new HorizontalComp(List.of(content))
|
||||
.styleClass("content")
|
||||
.apply(struc -> struc.get().setFillHeight(true))
|
||||
.hide(BindingsHelper.persist(Bindings.or(
|
||||
.hide(Bindings.or(
|
||||
Bindings.not(expanded),
|
||||
Bindings.size(section.getAllChildren()).isEqualTo(0)))));
|
||||
Bindings.size(section.getAllChildren()).isEqualTo(0))));
|
||||
|
||||
var vert = new VerticalComp(list);
|
||||
if (condensedStyle) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package io.xpipe.app.comp.store;
|
||||
|
||||
import io.xpipe.app.core.AppCache;
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.fxcomps.util.ListBindingsHelper;
|
||||
import io.xpipe.app.issue.ErrorEvent;
|
||||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
|
@ -273,7 +273,7 @@ public class StoreViewState {
|
|||
return o1.getName().compareToIgnoreCase(o2.getName());
|
||||
}
|
||||
};
|
||||
return BindingsHelper.filteredContentBinding(
|
||||
return ListBindingsHelper.filteredContentBinding(
|
||||
categories, cat -> root == null || cat.getRoot().equals(root))
|
||||
.sorted(comparator);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package io.xpipe.app.core;
|
|||
import io.xpipe.app.comp.base.MarkdownComp;
|
||||
import io.xpipe.app.core.mode.OperationMode;
|
||||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
|
@ -98,7 +97,7 @@ public class AppGreetings {
|
|||
alert.getButtonTypes().add(buttonType);
|
||||
|
||||
Button button = (Button) alert.getDialogPane().lookupButton(buttonType);
|
||||
button.disableProperty().bind(BindingsHelper.persist(accepted.not()));
|
||||
button.disableProperty().bind(accepted.not());
|
||||
}
|
||||
|
||||
alert.getButtonTypes().add(ButtonType.CANCEL);
|
||||
|
|
|
@ -2,7 +2,7 @@ package io.xpipe.app.core;
|
|||
|
||||
import io.xpipe.app.comp.base.ModalOverlayComp;
|
||||
import io.xpipe.app.ext.PrefsChoiceValue;
|
||||
import io.xpipe.app.fxcomps.impl.FancyTooltipAugment;
|
||||
import io.xpipe.app.fxcomps.impl.TooltipAugment;
|
||||
import io.xpipe.app.issue.ErrorEvent;
|
||||
import io.xpipe.app.issue.TrackEvent;
|
||||
import io.xpipe.app.prefs.AppPrefs;
|
||||
|
@ -116,7 +116,7 @@ public class AppI18n {
|
|||
|| caller.equals(ModuleHelper.class)
|
||||
|| caller.equals(ModalOverlayComp.class)
|
||||
|| caller.equals(AppI18n.class)
|
||||
|| caller.equals(FancyTooltipAugment.class)
|
||||
|| caller.equals(TooltipAugment.class)
|
||||
|| caller.equals(PrefsChoiceValue.class)
|
||||
|| caller.equals(Translatable.class)
|
||||
|| caller.equals(AppWindowHelper.class)
|
||||
|
|
|
@ -5,7 +5,6 @@ import io.xpipe.app.browser.BrowserModel;
|
|||
import io.xpipe.app.comp.DeveloperTabComp;
|
||||
import io.xpipe.app.comp.store.StoreLayoutComp;
|
||||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
import io.xpipe.app.prefs.AppPrefsComp;
|
||||
import io.xpipe.app.util.LicenseProvider;
|
||||
import javafx.beans.property.Property;
|
||||
|
@ -30,13 +29,11 @@ public class AppLayoutModel {
|
|||
private final List<Entry> entries;
|
||||
|
||||
private final Property<Entry> selected;
|
||||
private final ObservableValue<Entry> selectedWrapper;
|
||||
|
||||
public AppLayoutModel(SavedState savedState) {
|
||||
this.savedState = savedState;
|
||||
this.entries = createEntryList();
|
||||
this.selected = new SimpleObjectProperty<>(entries.get(1));
|
||||
this.selectedWrapper = PlatformThread.sync(selected);
|
||||
}
|
||||
|
||||
public static AppLayoutModel get() {
|
||||
|
@ -53,14 +50,10 @@ public class AppLayoutModel {
|
|||
INSTANCE = null;
|
||||
}
|
||||
|
||||
public Property<Entry> getSelectedInternal() {
|
||||
public Property<Entry> getSelected() {
|
||||
return selected;
|
||||
}
|
||||
|
||||
public ObservableValue<Entry> getSelected() {
|
||||
return selectedWrapper;
|
||||
}
|
||||
|
||||
public void selectBrowser() {
|
||||
selected.setValue(entries.getFirst());
|
||||
}
|
||||
|
|
|
@ -4,7 +4,8 @@ import atlantafx.base.controls.Spacer;
|
|||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.fxcomps.augment.Augment;
|
||||
import io.xpipe.app.fxcomps.augment.GrowAugment;
|
||||
import io.xpipe.app.fxcomps.impl.FancyTooltipAugment;
|
||||
import io.xpipe.app.fxcomps.impl.TooltipAugment;
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.fxcomps.util.Shortcuts;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
|
@ -143,6 +144,7 @@ public abstract class Comp<S extends CompStructure<?>> {
|
|||
public Comp<S> hide(ObservableValue<Boolean> o) {
|
||||
return apply(struc -> {
|
||||
var region = struc.get();
|
||||
BindingsHelper.preserve(region, o);
|
||||
o.subscribe(n -> {
|
||||
if (!n) {
|
||||
region.setVisible(true);
|
||||
|
@ -188,11 +190,11 @@ public abstract class Comp<S extends CompStructure<?>> {
|
|||
}
|
||||
|
||||
public Comp<S> tooltip(ObservableValue<String> text) {
|
||||
return apply(new FancyTooltipAugment<>(text));
|
||||
return apply(new TooltipAugment<>(text));
|
||||
}
|
||||
|
||||
public Comp<S> tooltipKey(String key) {
|
||||
return apply(new FancyTooltipAugment<>(key));
|
||||
return apply(new TooltipAugment<>(key));
|
||||
}
|
||||
|
||||
public Region createRegion() {
|
||||
|
@ -201,6 +203,8 @@ public abstract class Comp<S extends CompStructure<?>> {
|
|||
|
||||
public S createStructure() {
|
||||
S struc = createBase();
|
||||
// Make comp last at least as long as region
|
||||
BindingsHelper.preserve(struc.get(), this);
|
||||
if (augments != null) {
|
||||
for (var a : augments) {
|
||||
a.augment(struc);
|
||||
|
|
|
@ -4,7 +4,7 @@ import io.xpipe.app.core.AppI18n;
|
|||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.fxcomps.CompStructure;
|
||||
import io.xpipe.app.fxcomps.SimpleCompStructure;
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.fxcomps.util.ListBindingsHelper;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
import io.xpipe.app.util.Translatable;
|
||||
import javafx.beans.property.Property;
|
||||
|
@ -77,7 +77,7 @@ public class ChoiceComp<T> extends Comp<CompStructure<ComboBox<T>>> {
|
|||
list.add(null);
|
||||
}
|
||||
|
||||
BindingsHelper.setContent(cb.getItems(), list);
|
||||
ListBindingsHelper.setContent(cb.getItems(), list);
|
||||
});
|
||||
|
||||
cb.valueProperty().addListener((observable, oldValue, newValue) -> {
|
||||
|
|
|
@ -6,7 +6,6 @@ import io.xpipe.app.comp.base.ButtonComp;
|
|||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.core.AppWindowHelper;
|
||||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.issue.ErrorEvent;
|
||||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.app.storage.ContextualFileReference;
|
||||
|
@ -65,7 +64,7 @@ public class ContextualFileReferenceChoiceComp extends SimpleComp {
|
|||
.styleClass(Styles.CENTER_PILL)
|
||||
.grow(false, true);
|
||||
|
||||
var canGitShare = BindingsHelper.persist(Bindings.createBooleanBinding(
|
||||
var canGitShare = Bindings.createBooleanBinding(
|
||||
() -> {
|
||||
if (!AppPrefs.get().enableGitStorage().get()
|
||||
|| filePath.getValue() == null
|
||||
|
@ -76,7 +75,7 @@ public class ContextualFileReferenceChoiceComp extends SimpleComp {
|
|||
return true;
|
||||
},
|
||||
filePath,
|
||||
AppPrefs.get().enableGitStorage()));
|
||||
AppPrefs.get().enableGitStorage());
|
||||
var gitShareButton = new ButtonComp(null, new FontIcon("mdi2g-git"), () -> {
|
||||
if (filePath.getValue() == null || filePath.getValue().isBlank() || !canGitShare.get()) {
|
||||
return;
|
||||
|
@ -107,7 +106,7 @@ public class ContextualFileReferenceChoiceComp extends SimpleComp {
|
|||
ErrorEvent.fromThrowable(e).handle();
|
||||
}
|
||||
});
|
||||
gitShareButton.apply(new FancyTooltipAugment<>("gitShareFileTooltip"));
|
||||
gitShareButton.apply(new TooltipAugment<>("gitShareFileTooltip"));
|
||||
gitShareButton.styleClass(Styles.RIGHT_PILL).grow(false, true);
|
||||
|
||||
var layout = new HorizontalComp(List.of(fileNameComp, fileBrowseButton, gitShareButton))
|
||||
|
|
|
@ -27,11 +27,11 @@ public class DataStoreFlowChoiceComp extends SimpleComp {
|
|||
map.put(DataFlow.INPUT_OUTPUT, AppI18n.observable("app.inout"));
|
||||
return new ToggleGroupComp<>(selected, new SimpleObjectProperty<>(map))
|
||||
.apply(struc -> {
|
||||
new FancyTooltipAugment<>("app.inputDescription")
|
||||
new TooltipAugment<>("app.inputDescription")
|
||||
.augment(struc.get().getChildren().get(0));
|
||||
new FancyTooltipAugment<>("app.outputDescription")
|
||||
new TooltipAugment<>("app.outputDescription")
|
||||
.augment(struc.get().getChildren().get(1));
|
||||
new FancyTooltipAugment<>("app.inoutDescription")
|
||||
new TooltipAugment<>("app.inoutDescription")
|
||||
.augment(struc.get().getChildren().get(2));
|
||||
})
|
||||
.createRegion();
|
||||
|
|
|
@ -25,7 +25,6 @@ public class LabelComp extends Comp<CompStructure<Label>> {
|
|||
@Override
|
||||
public CompStructure<Label> createBase() {
|
||||
var label = new Label();
|
||||
BindingsHelper.linkPersistently(label,text);
|
||||
text.subscribe(t -> {
|
||||
PlatformThread.runLaterIfNeeded(() -> label.setText(t));
|
||||
});
|
||||
|
|
|
@ -11,7 +11,7 @@ import io.xpipe.app.core.AppI18n;
|
|||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
import io.xpipe.app.fxcomps.augment.ContextMenuAugment;
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.fxcomps.util.ListBindingsHelper;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStoreCategory;
|
||||
import io.xpipe.app.util.ContextMenuHelper;
|
||||
|
@ -76,7 +76,7 @@ public class StoreCategoryComp extends SimpleComp {
|
|||
showing.bind(cm.showingProperty());
|
||||
return cm;
|
||||
}));
|
||||
var shownList = BindingsHelper.filteredContentBinding(
|
||||
var shownList = ListBindingsHelper.filteredContentBinding(
|
||||
category.getContainedEntries(),
|
||||
storeEntryWrapper -> {
|
||||
return storeEntryWrapper.shouldShow(
|
||||
|
@ -91,9 +91,8 @@ public class StoreCategoryComp extends SimpleComp {
|
|||
Comp.hspacer(4),
|
||||
Comp.of(() -> name),
|
||||
Comp.hspacer(),
|
||||
count.hide(BindingsHelper.persist(hover.or(showing).or(focus))),
|
||||
settings.hide(
|
||||
BindingsHelper.persist(hover.not().and(showing.not()).and(focus.not())))));
|
||||
count.hide(hover.or(showing).or(focus)),
|
||||
settings.hide(hover.not().and(showing.not()).and(focus.not()))));
|
||||
h.padding(new Insets(0, 10, 0, (category.getDepth() * 10)));
|
||||
|
||||
var categoryButton = new ButtonComp(null, h.createRegion(), category::select)
|
||||
|
|
|
@ -3,22 +3,21 @@ package io.xpipe.app.fxcomps.impl;
|
|||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.fxcomps.CompStructure;
|
||||
import io.xpipe.app.fxcomps.augment.Augment;
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
import io.xpipe.app.fxcomps.util.Shortcuts;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.scene.control.Tooltip;
|
||||
|
||||
public class FancyTooltipAugment<S extends CompStructure<?>> implements Augment<S> {
|
||||
public class TooltipAugment<S extends CompStructure<?>> implements Augment<S> {
|
||||
|
||||
private final ObservableValue<String> text;
|
||||
|
||||
public FancyTooltipAugment(ObservableValue<String> text) {
|
||||
public TooltipAugment(ObservableValue<String> text) {
|
||||
this.text = PlatformThread.sync(text);
|
||||
}
|
||||
|
||||
public FancyTooltipAugment(String key) {
|
||||
public TooltipAugment(String key) {
|
||||
this.text = AppI18n.observable(key);
|
||||
}
|
||||
|
||||
|
@ -31,9 +30,9 @@ public class FancyTooltipAugment<S extends CompStructure<?>> implements Augment<
|
|||
var binding = Bindings.createStringBinding(() -> {
|
||||
return text.getValue() + "\n\n" + s.getValue() + ": " + Shortcuts.getDisplayShortcut(region).getDisplayText();
|
||||
}, text, s);
|
||||
BindingsHelper.bindStrong(tt.textProperty(), binding);
|
||||
tt.textProperty().bind(binding);
|
||||
} else {
|
||||
BindingsHelper.bindStrong(tt.textProperty(),text);
|
||||
tt.textProperty().bind(text);
|
||||
}
|
||||
tt.setStyle("-fx-font-size: 11pt;");
|
||||
tt.setWrapText(true);
|
|
@ -1,103 +1,50 @@
|
|||
package io.xpipe.app.fxcomps.util;
|
||||
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.binding.Binding;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.ListBinding;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import lombok.Value;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.*;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@SuppressWarnings("InfiniteLoopStatement")
|
||||
public class BindingsHelper {
|
||||
|
||||
private static final Set<ReferenceEntry> REFERENCES = Collections.newSetFromMap(new ConcurrentHashMap<>());
|
||||
/*
|
||||
TODO: Proper cleanup. Maybe with a separate thread?
|
||||
*/
|
||||
private static final Map<WeakReference<Object>, Set<javafx.beans.Observable>> BINDINGS = new ConcurrentHashMap<>();
|
||||
|
||||
static {
|
||||
ThreadHelper.createPlatformThread("referenceGC", true, () -> {
|
||||
while (true) {
|
||||
for (ReferenceEntry reference : REFERENCES) {
|
||||
if (reference.canGc()) {
|
||||
/*
|
||||
TODO: Figure out why some bindings are garbage collected, even if they shouldn't
|
||||
*/
|
||||
// REFERENCES.remove(reference);
|
||||
REFERENCES.remove(reference);
|
||||
}
|
||||
}
|
||||
ThreadHelper.sleep(1000);
|
||||
|
||||
// Use for testing
|
||||
// System.gc();
|
||||
}
|
||||
})
|
||||
.start();
|
||||
}
|
||||
|
||||
public static <T> void bindStrong(Property<T> property, ObservableValue<T> value) {
|
||||
property.bind(value);
|
||||
linkPersistently(property, value);
|
||||
}
|
||||
|
||||
public static <T, V> void bindExclusive(
|
||||
Property<V> selected, Map<V, ? extends Property<T>> map, Property<T> toBind) {
|
||||
selected.addListener((c, o, n) -> {
|
||||
toBind.unbind();
|
||||
toBind.bind(map.get(n));
|
||||
});
|
||||
|
||||
toBind.bind(map.get(selected.getValue()));
|
||||
}
|
||||
|
||||
public static void linkPersistently(Object source, Object target) {
|
||||
public static void preserve(Object source, Object target) {
|
||||
REFERENCES.add(new ReferenceEntry(new WeakReference<>(source), target));
|
||||
}
|
||||
|
||||
public static <T extends Binding<?>> T persist(T binding) {
|
||||
var dependencies = new HashSet<javafx.beans.Observable>();
|
||||
while (dependencies.addAll(binding.getDependencies().stream()
|
||||
.map(o -> (javafx.beans.Observable) o)
|
||||
.toList())) {}
|
||||
dependencies.add(binding);
|
||||
BINDINGS.put(new WeakReference<>(binding), dependencies);
|
||||
return binding;
|
||||
}
|
||||
|
||||
public static <T extends ListBinding<?>> T persist(T binding) {
|
||||
var dependencies = new HashSet<javafx.beans.Observable>();
|
||||
while (dependencies.addAll(binding.getDependencies().stream()
|
||||
.map(o -> (javafx.beans.Observable) o)
|
||||
.toList())) {}
|
||||
dependencies.add(binding);
|
||||
BINDINGS.put(new WeakReference<>(binding), dependencies);
|
||||
return binding;
|
||||
}
|
||||
|
||||
public static <T> void bindContent(ObservableList<T> l1, ObservableList<? extends T> l2) {
|
||||
setContent(l1, l2);
|
||||
l2.addListener((ListChangeListener<? super T>) c -> {
|
||||
setContent(l1, l2);
|
||||
});
|
||||
}
|
||||
|
||||
public static <T, U> ObservableValue<U> map(
|
||||
ObservableValue<T> observableValue, Function<? super T, ? extends U> mapper) {
|
||||
return persist(Bindings.createObjectBinding(
|
||||
return Bindings.createObjectBinding(
|
||||
() -> {
|
||||
return mapper.apply(observableValue.getValue());
|
||||
},
|
||||
observableValue));
|
||||
observableValue);
|
||||
}
|
||||
|
||||
public static <T, U> ObservableValue<U> flatMap(
|
||||
|
@ -110,229 +57,10 @@ public class BindingsHelper {
|
|||
observableValue.addListener((observable, oldValue, newValue) -> {
|
||||
runnable.run();
|
||||
});
|
||||
linkPersistently(observableValue, prop);
|
||||
preserve(prop, observableValue);
|
||||
return prop;
|
||||
}
|
||||
|
||||
public static <T, U> ObservableValue<Boolean> anyMatch(List<? extends ObservableValue<Boolean>> l) {
|
||||
return BindingsHelper.persist(Bindings.createBooleanBinding(
|
||||
() -> {
|
||||
return l.stream().anyMatch(booleanObservableValue -> booleanObservableValue.getValue());
|
||||
},
|
||||
l.toArray(ObservableValue[]::new)));
|
||||
}
|
||||
|
||||
public static <T, V> void bindMappedContent(ObservableList<T> l1, ObservableList<V> l2, Function<V, T> map) {
|
||||
Runnable runnable = () -> {
|
||||
setContent(l1, l2.stream().map(map).toList());
|
||||
};
|
||||
runnable.run();
|
||||
l2.addListener((ListChangeListener<? super V>) c -> {
|
||||
runnable.run();
|
||||
});
|
||||
}
|
||||
|
||||
public static <T, V> ObservableList<T> mappedContentBinding(ObservableList<V> l2, Function<V, T> map) {
|
||||
ObservableList<T> l1 = FXCollections.observableList(new ArrayList<>());
|
||||
Runnable runnable = () -> {
|
||||
setContent(l1, l2.stream().map(map).toList());
|
||||
};
|
||||
runnable.run();
|
||||
l2.addListener((ListChangeListener<? super V>) c -> {
|
||||
runnable.run();
|
||||
});
|
||||
linkPersistently(l2, l1);
|
||||
return l1;
|
||||
}
|
||||
|
||||
public static <T, V> ObservableList<T> cachedMappedContentBinding(ObservableList<V> l2, Function<V, T> map) {
|
||||
var cache = new HashMap<V, T>();
|
||||
|
||||
ObservableList<T> l1 = FXCollections.observableList(new ArrayList<>());
|
||||
Runnable runnable = () -> {
|
||||
cache.keySet().removeIf(t -> !l2.contains(t));
|
||||
setContent(
|
||||
l1,
|
||||
l2.stream()
|
||||
.map(v -> {
|
||||
if (!cache.containsKey(v)) {
|
||||
cache.put(v, map.apply(v));
|
||||
}
|
||||
|
||||
return cache.get(v);
|
||||
})
|
||||
.toList());
|
||||
};
|
||||
runnable.run();
|
||||
l2.addListener((ListChangeListener<? super V>) c -> {
|
||||
runnable.run();
|
||||
});
|
||||
linkPersistently(l2, l1);
|
||||
return l1;
|
||||
}
|
||||
|
||||
public static <T, V> ObservableList<T> cachedMappedContentBinding(
|
||||
ObservableList<V> all, ObservableList<V> shown, Function<V, T> map) {
|
||||
var cache = new HashMap<V, T>();
|
||||
|
||||
ObservableList<T> l1 = FXCollections.observableList(new ArrayList<>());
|
||||
Runnable runnable = () -> {
|
||||
cache.keySet().removeIf(t -> !all.contains(t));
|
||||
setContent(
|
||||
l1,
|
||||
shown.stream()
|
||||
.map(v -> {
|
||||
if (!cache.containsKey(v)) {
|
||||
cache.put(v, map.apply(v));
|
||||
}
|
||||
|
||||
return cache.get(v);
|
||||
})
|
||||
.toList());
|
||||
};
|
||||
runnable.run();
|
||||
shown.addListener((ListChangeListener<? super V>) c -> {
|
||||
runnable.run();
|
||||
});
|
||||
linkPersistently(all, l1);
|
||||
linkPersistently(shown, l1);
|
||||
return l1;
|
||||
}
|
||||
|
||||
public static <T, U> ObservableValue<U> mappedBinding(
|
||||
ObservableValue<T> observableValue, Function<? super T, ? extends ObservableValue<? extends U>> mapper) {
|
||||
var binding = (Binding<U>) observableValue.flatMap(mapper);
|
||||
return persist(binding);
|
||||
}
|
||||
|
||||
public static <V> ObservableList<V> orderedContentBinding(
|
||||
ObservableList<V> l2, Comparator<V> comp, Observable... observables) {
|
||||
return orderedContentBinding(
|
||||
l2,
|
||||
Bindings.createObjectBinding(
|
||||
() -> {
|
||||
return new Comparator<>() {
|
||||
@Override
|
||||
public int compare(V o1, V o2) {
|
||||
return comp.compare(o1, o2);
|
||||
}
|
||||
};
|
||||
},
|
||||
observables));
|
||||
}
|
||||
|
||||
// public static <T,U> ObservableValue<U> mappedBinding(ObservableValue<T> observableValue, Function<? super T, ?
|
||||
// extends ObservableValue<? extends U>> mapper) {
|
||||
// var v = new SimpleObjectProperty<U>();
|
||||
// observableValue, val -> {
|
||||
// v.unbind();
|
||||
// v.bind(mapper.apply(val));
|
||||
// });
|
||||
// return v;
|
||||
// }
|
||||
|
||||
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.getValue()).toList());
|
||||
};
|
||||
runnable.run();
|
||||
l2.addListener((ListChangeListener<? super V>) c -> {
|
||||
runnable.run();
|
||||
});
|
||||
comp.addListener((observable, oldValue, newValue) -> {
|
||||
runnable.run();
|
||||
});
|
||||
linkPersistently(l2, l1);
|
||||
return l1;
|
||||
}
|
||||
|
||||
public static <V> ObservableList<V> filteredContentBinding(ObservableList<V> l2, Predicate<V> predicate) {
|
||||
return filteredContentBinding(l2, new SimpleObjectProperty<>(predicate));
|
||||
}
|
||||
|
||||
public static <V> ObservableList<V> filteredContentBinding(
|
||||
ObservableList<V> l2, Predicate<V> predicate, Observable... observables) {
|
||||
return filteredContentBinding(
|
||||
l2,
|
||||
Bindings.createObjectBinding(
|
||||
() -> {
|
||||
return new Predicate<>() {
|
||||
@Override
|
||||
public boolean test(V v) {
|
||||
return predicate.test(v);
|
||||
}
|
||||
};
|
||||
},
|
||||
Arrays.stream(observables).filter(Objects::nonNull).toArray(Observable[]::new)));
|
||||
}
|
||||
|
||||
public static <V> ObservableList<V> filteredContentBinding(
|
||||
ObservableList<V> l2, ObservableValue<Predicate<V>> predicate) {
|
||||
ObservableList<V> l1 = FXCollections.observableList(new ArrayList<>());
|
||||
Runnable runnable = () -> {
|
||||
setContent(
|
||||
l1,
|
||||
predicate.getValue() != null
|
||||
? l2.stream().filter(predicate.getValue()).toList()
|
||||
: l2);
|
||||
};
|
||||
runnable.run();
|
||||
l2.addListener((ListChangeListener<? super V>) c -> {
|
||||
runnable.run();
|
||||
});
|
||||
predicate.addListener((c, o, n) -> {
|
||||
runnable.run();
|
||||
});
|
||||
linkPersistently(l2, l1);
|
||||
return l1;
|
||||
}
|
||||
|
||||
public static <T> void setContent(ObservableList<T> target, List<? extends T> newList) {
|
||||
if (target.equals(newList)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (target.size() == 0) {
|
||||
target.setAll(newList);
|
||||
return;
|
||||
}
|
||||
|
||||
if (newList.size() == 0) {
|
||||
target.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
var targetSet = new HashSet<>(target);
|
||||
var newSet = new HashSet<>(newList);
|
||||
|
||||
// Only add missing element
|
||||
if (target.size() + 1 == newList.size() && newSet.containsAll(targetSet)) {
|
||||
var l = new HashSet<>(newSet);
|
||||
l.removeAll(targetSet);
|
||||
if (l.size() > 0) {
|
||||
var found = l.iterator().next();
|
||||
var index = newList.indexOf(found);
|
||||
target.add(index, found);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Only remove not needed element
|
||||
if (target.size() - 1 == newList.size() && targetSet.containsAll(newSet)) {
|
||||
var l = new HashSet<>(targetSet);
|
||||
l.removeAll(newSet);
|
||||
if (l.size() > 0) {
|
||||
target.remove(l.iterator().next());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Other cases are more difficult
|
||||
target.setAll(newList);
|
||||
}
|
||||
|
||||
@Value
|
||||
private static class ReferenceEntry {
|
||||
|
||||
|
|
|
@ -0,0 +1,190 @@
|
|||
package io.xpipe.app.fxcomps.util;
|
||||
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class ListBindingsHelper {
|
||||
|
||||
public static <T> void bindContent(ObservableList<T> l1, ObservableList<? extends T> l2) {
|
||||
setContent(l1, l2);
|
||||
l2.addListener((ListChangeListener<? super T>) c -> {
|
||||
setContent(l1, l2);
|
||||
});
|
||||
}
|
||||
|
||||
public static <T, U> ObservableValue<Boolean> anyMatch(List<? extends ObservableValue<Boolean>> l) {
|
||||
return Bindings.createBooleanBinding(
|
||||
() -> {
|
||||
return l.stream().anyMatch(booleanObservableValue -> booleanObservableValue.getValue());
|
||||
},
|
||||
l.toArray(ObservableValue[]::new));
|
||||
}
|
||||
|
||||
public static <T, V> ObservableList<T> mappedContentBinding(ObservableList<V> l2, Function<V, T> map) {
|
||||
ObservableList<T> l1 = FXCollections.observableList(new ArrayList<>());
|
||||
Runnable runnable = () -> {
|
||||
setContent(l1, l2.stream().map(map).toList());
|
||||
};
|
||||
runnable.run();
|
||||
l2.addListener((ListChangeListener<? super V>) c -> {
|
||||
runnable.run();
|
||||
});
|
||||
BindingsHelper.preserve(l1, l2);
|
||||
return l1;
|
||||
}
|
||||
|
||||
public static <T, V> ObservableList<T> cachedMappedContentBinding(
|
||||
ObservableList<V> all, ObservableList<V> shown, Function<V, T> map) {
|
||||
var cache = new HashMap<V, T>();
|
||||
|
||||
ObservableList<T> l1 = FXCollections.observableList(new ArrayList<>());
|
||||
Runnable runnable = () -> {
|
||||
cache.keySet().removeIf(t -> !all.contains(t));
|
||||
setContent(
|
||||
l1,
|
||||
shown.stream()
|
||||
.map(v -> {
|
||||
if (!cache.containsKey(v)) {
|
||||
cache.put(v, map.apply(v));
|
||||
}
|
||||
|
||||
return cache.get(v);
|
||||
})
|
||||
.toList());
|
||||
};
|
||||
runnable.run();
|
||||
shown.addListener((ListChangeListener<? super V>) c -> {
|
||||
runnable.run();
|
||||
});
|
||||
BindingsHelper.preserve(l1, all);
|
||||
BindingsHelper.preserve(l1, shown);
|
||||
return l1;
|
||||
}
|
||||
|
||||
public static <V> ObservableList<V> orderedContentBinding(
|
||||
ObservableList<V> l2, Comparator<V> comp, Observable... observables) {
|
||||
return orderedContentBinding(
|
||||
l2,
|
||||
Bindings.createObjectBinding(
|
||||
() -> {
|
||||
return new Comparator<>() {
|
||||
@Override
|
||||
public int compare(V o1, V o2) {
|
||||
return comp.compare(o1, o2);
|
||||
}
|
||||
};
|
||||
},
|
||||
observables));
|
||||
}
|
||||
|
||||
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.getValue()).toList());
|
||||
};
|
||||
runnable.run();
|
||||
l2.addListener((ListChangeListener<? super V>) c -> {
|
||||
runnable.run();
|
||||
});
|
||||
comp.addListener((observable, oldValue, newValue) -> {
|
||||
runnable.run();
|
||||
});
|
||||
BindingsHelper.preserve(l1, l2);
|
||||
return l1;
|
||||
}
|
||||
|
||||
public static <V> ObservableList<V> filteredContentBinding(ObservableList<V> l2, Predicate<V> predicate) {
|
||||
return filteredContentBinding(l2, new SimpleObjectProperty<>(predicate));
|
||||
}
|
||||
|
||||
public static <V> ObservableList<V> filteredContentBinding(
|
||||
ObservableList<V> l2, Predicate<V> predicate, Observable... observables) {
|
||||
return filteredContentBinding(
|
||||
l2,
|
||||
Bindings.createObjectBinding(
|
||||
() -> {
|
||||
return new Predicate<>() {
|
||||
@Override
|
||||
public boolean test(V v) {
|
||||
return predicate.test(v);
|
||||
}
|
||||
};
|
||||
},
|
||||
Arrays.stream(observables).filter(Objects::nonNull).toArray(Observable[]::new)));
|
||||
}
|
||||
|
||||
public static <V> ObservableList<V> filteredContentBinding(
|
||||
ObservableList<V> l2, ObservableValue<Predicate<V>> predicate) {
|
||||
ObservableList<V> l1 = FXCollections.observableList(new ArrayList<>());
|
||||
Runnable runnable = () -> {
|
||||
setContent(
|
||||
l1,
|
||||
predicate.getValue() != null
|
||||
? l2.stream().filter(predicate.getValue()).toList()
|
||||
: l2);
|
||||
};
|
||||
runnable.run();
|
||||
l2.addListener((ListChangeListener<? super V>) c -> {
|
||||
runnable.run();
|
||||
});
|
||||
predicate.addListener((c, o, n) -> {
|
||||
runnable.run();
|
||||
});
|
||||
BindingsHelper.preserve(l1, l2);
|
||||
return l1;
|
||||
}
|
||||
|
||||
public static <T> void setContent(ObservableList<T> target, List<? extends T> newList) {
|
||||
if (target.equals(newList)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (target.size() == 0) {
|
||||
target.setAll(newList);
|
||||
return;
|
||||
}
|
||||
|
||||
if (newList.size() == 0) {
|
||||
target.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
var targetSet = new HashSet<>(target);
|
||||
var newSet = new HashSet<>(newList);
|
||||
|
||||
// Only add missing element
|
||||
if (target.size() + 1 == newList.size() && newSet.containsAll(targetSet)) {
|
||||
var l = new HashSet<>(newSet);
|
||||
l.removeAll(targetSet);
|
||||
if (l.size() > 0) {
|
||||
var found = l.iterator().next();
|
||||
var index = newList.indexOf(found);
|
||||
target.add(index, found);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Only remove not needed element
|
||||
if (target.size() - 1 == newList.size() && targetSet.containsAll(newSet)) {
|
||||
var l = new HashSet<>(targetSet);
|
||||
l.removeAll(newSet);
|
||||
if (l.size() > 0) {
|
||||
target.remove(l.iterator().next());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Other cases are more difficult
|
||||
target.setAll(newList);
|
||||
}
|
||||
}
|
|
@ -85,7 +85,6 @@ public class PlatformThread {
|
|||
ov.removeListener(invListenerMap.getOrDefault(listener, listener));
|
||||
}
|
||||
};
|
||||
BindingsHelper.linkPersistently(obs, ov);
|
||||
return obs;
|
||||
}
|
||||
|
||||
|
@ -272,7 +271,6 @@ public class PlatformThread {
|
|||
ol.removeListener(invListenerMap.getOrDefault(listener, listener));
|
||||
}
|
||||
};
|
||||
BindingsHelper.linkPersistently(obs, ol);
|
||||
return obs;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@ import io.xpipe.app.fxcomps.impl.ChoiceComp;
|
|||
import io.xpipe.app.fxcomps.impl.HorizontalComp;
|
||||
import io.xpipe.app.fxcomps.impl.StackComp;
|
||||
import io.xpipe.app.fxcomps.impl.TextFieldComp;
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.terminal.ExternalTerminalType;
|
||||
import io.xpipe.app.util.Hyperlinks;
|
||||
import io.xpipe.app.util.OptionsBuilder;
|
||||
|
@ -66,14 +65,14 @@ public class TerminalCategory extends AppPrefsCategory {
|
|||
|
||||
Hyperlinks.open(t.getWebsite());
|
||||
});
|
||||
var visitVisible = BindingsHelper.persist(Bindings.createBooleanBinding(() -> {
|
||||
var visitVisible = Bindings.createBooleanBinding(() -> {
|
||||
var t = prefs.terminalType().getValue();
|
||||
if (t == null || t.getWebsite() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}, prefs.terminalType()));
|
||||
}, prefs.terminalType());
|
||||
visit.visible(visitVisible);
|
||||
|
||||
return new HorizontalComp(List.of(c, visit)).apply(struc -> {
|
||||
|
|
Loading…
Reference in a new issue