mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-21 15:10:23 +00:00
Rework categories
This commit is contained in:
parent
a65a0bd1b0
commit
be684d7b72
19 changed files with 228 additions and 109 deletions
|
@ -364,9 +364,7 @@ public class BrowserSessionTabsComp extends SimpleComp {
|
|||
|
||||
StackPane c = (StackPane) tabs.lookup("#" + id + " .tab-container");
|
||||
c.getStyleClass().add("color-box");
|
||||
var color = DataStorage.get()
|
||||
.getRootForEntry(model.getEntry().get())
|
||||
.getColor();
|
||||
var color = DataStorage.get().getEffectiveColor(model.getEntry().get());
|
||||
if (color != null) {
|
||||
c.getStyleClass().add(color.getId());
|
||||
}
|
||||
|
|
|
@ -2,6 +2,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.PlatformThread;
|
||||
|
||||
import javafx.beans.property.Property;
|
||||
|
@ -15,7 +16,7 @@ import lombok.Value;
|
|||
|
||||
import java.util.Objects;
|
||||
|
||||
public class LazyTextFieldComp extends Comp<LazyTextFieldComp.Structure> {
|
||||
public class LazyTextFieldComp extends Comp<CompStructure<TextField>> {
|
||||
|
||||
private final Property<String> currentValue;
|
||||
private final Property<String> appliedValue;
|
||||
|
@ -26,8 +27,7 @@ public class LazyTextFieldComp extends Comp<LazyTextFieldComp.Structure> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public LazyTextFieldComp.Structure createBase() {
|
||||
var sp = new StackPane();
|
||||
public CompStructure<TextField> createBase() {
|
||||
var r = new TextField();
|
||||
|
||||
r.setOnKeyPressed(ke -> {
|
||||
|
@ -48,23 +48,14 @@ public class LazyTextFieldComp extends Comp<LazyTextFieldComp.Structure> {
|
|||
}
|
||||
});
|
||||
|
||||
sp.focusedProperty().addListener((c, o, n) -> {
|
||||
if (n) {
|
||||
r.setDisable(false);
|
||||
r.requestFocus();
|
||||
}
|
||||
});
|
||||
|
||||
// Handles external updates
|
||||
PlatformThread.sync(appliedValue).addListener((observable, oldValue, n) -> {
|
||||
currentValue.setValue(n);
|
||||
});
|
||||
|
||||
r.setPrefWidth(0);
|
||||
sp.getChildren().add(r);
|
||||
sp.prefWidthProperty().bind(r.prefWidthProperty());
|
||||
sp.prefHeightProperty().bind(r.prefHeightProperty());
|
||||
r.setMinWidth(0);
|
||||
r.setDisable(true);
|
||||
r.prefWidthProperty().bind(r.minWidthProperty());
|
||||
|
||||
currentValue.subscribe(n -> {
|
||||
PlatformThread.runLaterIfNeeded(() -> {
|
||||
|
@ -86,7 +77,7 @@ public class LazyTextFieldComp extends Comp<LazyTextFieldComp.Structure> {
|
|||
}
|
||||
});
|
||||
r.getStyleClass().add("lazy-text-field-comp");
|
||||
return new Structure(sp, r);
|
||||
return new SimpleCompStructure<>(r);
|
||||
}
|
||||
|
||||
@Value
|
||||
|
|
|
@ -6,6 +6,7 @@ import io.xpipe.app.prefs.AppPrefs;
|
|||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStoreCategory;
|
||||
|
||||
import io.xpipe.app.storage.DataColor;
|
||||
import javafx.beans.property.*;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
|
@ -29,6 +30,7 @@ public class StoreCategoryWrapper {
|
|||
private final ObservableList<StoreCategoryWrapper> children;
|
||||
private final ObservableList<StoreEntryWrapper> containedEntries;
|
||||
private final BooleanProperty expanded = new SimpleBooleanProperty();
|
||||
private final Property<DataColor> color = new SimpleObjectProperty<>();
|
||||
|
||||
public StoreCategoryWrapper(DataStoreCategory category) {
|
||||
var d = 0;
|
||||
|
@ -51,6 +53,7 @@ public class StoreCategoryWrapper {
|
|||
this.share = new SimpleObjectProperty<>(category.isShare());
|
||||
this.children = FXCollections.observableArrayList();
|
||||
this.containedEntries = FXCollections.observableArrayList();
|
||||
this.color.setValue(category.getColor());
|
||||
setupListeners();
|
||||
}
|
||||
|
||||
|
@ -130,6 +133,7 @@ public class StoreCategoryWrapper {
|
|||
sortMode.setValue(category.getSortMode());
|
||||
share.setValue(category.isShare());
|
||||
expanded.setValue(category.isExpanded());
|
||||
color.setValue(category.getColor());
|
||||
|
||||
containedEntries.setAll(StoreViewState.get().getAllEntries().getList().stream()
|
||||
.filter(entry -> {
|
||||
|
|
|
@ -17,7 +17,7 @@ import io.xpipe.app.fxcomps.util.DerivedObservableList;
|
|||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStoreColor;
|
||||
import io.xpipe.app.storage.DataColor;
|
||||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
import io.xpipe.app.update.XPipeDistributionType;
|
||||
import io.xpipe.app.util.*;
|
||||
|
@ -345,7 +345,7 @@ public abstract class StoreEntryComp extends SimpleComp {
|
|||
event.consume();
|
||||
});
|
||||
color.getItems().add(none);
|
||||
Arrays.stream(DataStoreColor.values()).forEach(dataStoreColor -> {
|
||||
Arrays.stream(DataColor.values()).forEach(dataStoreColor -> {
|
||||
MenuItem m = new MenuItem(DataStoreFormatter.capitalize(dataStoreColor.getId()));
|
||||
m.setOnAction(event -> {
|
||||
getWrapper().getEntry().setColor(dataStoreColor);
|
||||
|
|
|
@ -6,7 +6,7 @@ import io.xpipe.app.issue.ErrorEvent;
|
|||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStoreCategory;
|
||||
import io.xpipe.app.storage.DataStoreColor;
|
||||
import io.xpipe.app.storage.DataColor;
|
||||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
|
||||
|
@ -36,7 +36,7 @@ public class StoreEntryWrapper {
|
|||
private final BooleanProperty expanded = new SimpleBooleanProperty();
|
||||
private final Property<Object> persistentState = new SimpleObjectProperty<>();
|
||||
private final Property<Map<String, Object>> cache = new SimpleObjectProperty<>(Map.of());
|
||||
private final Property<DataStoreColor> color = new SimpleObjectProperty<>();
|
||||
private final Property<DataColor> color = new SimpleObjectProperty<>();
|
||||
private final Property<StoreCategoryWrapper> category = new SimpleObjectProperty<>();
|
||||
private final Property<String> summary = new SimpleObjectProperty<>();
|
||||
private final Property<StoreNotes> notes;
|
||||
|
|
|
@ -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.storage.DataStoreColor;
|
||||
import io.xpipe.app.storage.DataColor;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
|
@ -175,7 +175,7 @@ public class StoreSectionComp extends Comp<CompStructure<VBox>> {
|
|||
}
|
||||
|
||||
var newList = new ArrayList<>(struc.get().getStyleClass());
|
||||
newList.removeIf(s -> Arrays.stream(DataStoreColor.values())
|
||||
newList.removeIf(s -> Arrays.stream(DataColor.values())
|
||||
.anyMatch(
|
||||
dataStoreColor -> dataStoreColor.getId().equals(s)));
|
||||
newList.remove("gray");
|
||||
|
|
|
@ -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.storage.DataStoreColor;
|
||||
import io.xpipe.app.storage.DataColor;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
|
@ -168,7 +168,7 @@ public class StoreSectionMiniComp extends Comp<CompStructure<VBox>> {
|
|||
return;
|
||||
}
|
||||
|
||||
struc.get().getStyleClass().removeIf(s -> Arrays.stream(DataStoreColor.values())
|
||||
struc.get().getStyleClass().removeIf(s -> Arrays.stream(DataColor.values())
|
||||
.anyMatch(dataStoreColor ->
|
||||
dataStoreColor.getId().equals(s)));
|
||||
struc.get().getStyleClass().remove("gray");
|
||||
|
|
|
@ -214,8 +214,9 @@ public class AppWindowHelper {
|
|||
var r = scene.getRoot();
|
||||
if (r != null) {
|
||||
var acc = Platform.isAccessibilityActive();
|
||||
r.pseudoClassStateChanged(PseudoClass.getPseudoClass("key-navigation"), kb && !acc);
|
||||
r.pseudoClassStateChanged(PseudoClass.getPseudoClass("normal-navigation"), !kb && !acc);
|
||||
// This property is broken on some systems
|
||||
r.pseudoClassStateChanged(PseudoClass.getPseudoClass("key-navigation"), kb);
|
||||
r.pseudoClassStateChanged(PseudoClass.getPseudoClass("normal-navigation"), !kb);
|
||||
r.pseudoClassStateChanged(PseudoClass.getPseudoClass("accessibility-navigation"), acc);
|
||||
}
|
||||
});
|
||||
|
@ -223,7 +224,8 @@ public class AppWindowHelper {
|
|||
Platform.accessibilityActiveProperty().addListener((observable, oldValue, newValue) -> {
|
||||
var r = scene.getRoot();
|
||||
if (r != null) {
|
||||
r.pseudoClassStateChanged(PseudoClass.getPseudoClass("key-navigation"), false);
|
||||
// This property is broken on some systems
|
||||
r.pseudoClassStateChanged(PseudoClass.getPseudoClass("key-navigation"), true);
|
||||
r.pseudoClassStateChanged(PseudoClass.getPseudoClass("normal-navigation"), false);
|
||||
r.pseudoClassStateChanged(PseudoClass.getPseudoClass("accessibility-navigation"), true);
|
||||
}
|
||||
|
|
|
@ -12,25 +12,30 @@ 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.DerivedObservableList;
|
||||
import io.xpipe.app.storage.DataColor;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStoreCategory;
|
||||
import io.xpipe.app.util.ContextMenuHelper;
|
||||
|
||||
import io.xpipe.app.util.DataStoreFormatter;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.css.PseudoClass;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.ContextMenu;
|
||||
import javafx.scene.control.Menu;
|
||||
import javafx.scene.control.MenuItem;
|
||||
import javafx.scene.control.SeparatorMenuItem;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.KeyCodeCombination;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
import javafx.scene.input.MouseButton;
|
||||
import javafx.scene.layout.Region;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Value;
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
@ -45,43 +50,60 @@ public class StoreCategoryComp extends SimpleComp {
|
|||
|
||||
@Override
|
||||
protected Region createSimple() {
|
||||
var i = Bindings.createStringBinding(
|
||||
var name = new LazyTextFieldComp(category.nameProperty())
|
||||
.styleClass("name")
|
||||
.createRegion();
|
||||
var showing = new SimpleBooleanProperty();
|
||||
|
||||
var expandIcon = Bindings.createStringBinding(
|
||||
() -> {
|
||||
var exp = category.getExpanded().get() && category.getChildren().size() > 0;
|
||||
return exp ? "mdal-keyboard_arrow_down" : "mdal-keyboard_arrow_right";
|
||||
},
|
||||
category.getExpanded(), category.getChildren());
|
||||
var expandButton = new IconButtonComp(expandIcon, () -> {
|
||||
category.toggleExpanded();
|
||||
})
|
||||
.apply(struc -> AppFont.medium(struc.get()))
|
||||
.apply(struc -> {
|
||||
struc.get().setAlignment(Pos.CENTER);
|
||||
struc.get().setPadding(new Insets(-2, 0, 0, 0));
|
||||
struc.get().setFocusTraversable(false);
|
||||
})
|
||||
.styleClass("expand-button")
|
||||
.tooltipKey("expand", new KeyCodeCombination(KeyCode.SPACE));
|
||||
|
||||
var hover = new SimpleBooleanProperty();
|
||||
var statusIcon = Bindings.createStringBinding(
|
||||
() -> {
|
||||
if (hover.get()) {
|
||||
return "mdomz-settings";
|
||||
}
|
||||
|
||||
if (!DataStorage.get().supportsSharing()
|
||||
|| !category.getCategory().canShare()) {
|
||||
var exp = category.getExpanded().get() && category.getChildren().size() > 0;
|
||||
return exp ? "mdal-keyboard_arrow_down" : "mdal-keyboard_arrow_right";
|
||||
return "mdi2a-account-lock";
|
||||
}
|
||||
|
||||
return category.getShare().getValue() ? "mdi2g-git" : "mdi2a-account-cancel";
|
||||
},
|
||||
category.getShare(), category.getExpanded(), category.getChildren());
|
||||
var icon = new IconButtonComp(i, () -> {
|
||||
category.toggleExpanded();
|
||||
})
|
||||
category.getShare(), hover);
|
||||
var statusButton = new IconButtonComp(statusIcon)
|
||||
.apply(struc -> AppFont.small(struc.get()))
|
||||
.apply(struc -> {
|
||||
struc.get().setAlignment(Pos.CENTER);
|
||||
struc.get().setPadding(new Insets(0, 0, 6, 0));
|
||||
struc.get().setPadding(new Insets(0, 0, 7, 0));
|
||||
struc.get().setFocusTraversable(false);
|
||||
});
|
||||
var name = new LazyTextFieldComp(category.nameProperty())
|
||||
.apply(struc -> {
|
||||
struc.get().prefWidthProperty().unbind();
|
||||
struc.get().setPrefWidth(150);
|
||||
struc.getTextField().minWidthProperty().bind(struc.get().widthProperty());
|
||||
hover.bind(struc.get().hoverProperty());
|
||||
})
|
||||
.styleClass("name")
|
||||
.createRegion();
|
||||
var showing = new SimpleBooleanProperty();
|
||||
var settings = new IconButtonComp("mdomz-settings")
|
||||
.styleClass("settings")
|
||||
.apply(new ContextMenuAugment<>(
|
||||
mouseEvent -> mouseEvent.getButton() == MouseButton.PRIMARY, null, () -> {
|
||||
var cm = createContextMenu(name);
|
||||
showing.bind(cm.showingProperty());
|
||||
return cm;
|
||||
}));
|
||||
var cm = createContextMenu(name);
|
||||
showing.bind(cm.showingProperty());
|
||||
return cm;
|
||||
}))
|
||||
.styleClass("status-button");
|
||||
|
||||
var shownList = new DerivedObservableList<>(category.getContainedEntries(), true)
|
||||
.filtered(
|
||||
storeEntryWrapper -> {
|
||||
|
@ -91,18 +113,21 @@ public class StoreCategoryComp extends SimpleComp {
|
|||
StoreViewState.get().getFilterString())
|
||||
.getList();
|
||||
var count = new CountComp<>(shownList, category.getContainedEntries(), string -> "(" + string + ")");
|
||||
var hover = new SimpleBooleanProperty();
|
||||
|
||||
var showStatus = hover.or(new SimpleBooleanProperty(DataStorage.get().supportsSharing())).or(showing);
|
||||
var focus = new SimpleBooleanProperty();
|
||||
var h = new HorizontalComp(List.of(
|
||||
icon,
|
||||
Comp.hspacer(4),
|
||||
Comp.of(() -> name),
|
||||
Comp.hspacer(),
|
||||
count.hide(hover.or(showing).or(focus)),
|
||||
settings.hide(hover.not().and(showing.not()).and(focus.not()))));
|
||||
expandButton,
|
||||
Comp.hspacer(1),
|
||||
Comp.of(() -> name).hgrow(),
|
||||
Comp.hspacer(2),
|
||||
count,
|
||||
Comp.hspacer(7),
|
||||
statusButton.hide(showStatus.not())));
|
||||
h.padding(new Insets(0, 10, 0, (category.getDepth() * 10)));
|
||||
|
||||
var categoryButton = new ButtonComp(null, h.createRegion(), category::select)
|
||||
.focusTraversable()
|
||||
.styleClass("category-button")
|
||||
.apply(struc -> hover.bind(struc.get().hoverProperty()))
|
||||
.apply(struc -> focus.bind(struc.get().focusedProperty()))
|
||||
|
@ -112,12 +137,21 @@ public class StoreCategoryComp extends SimpleComp {
|
|||
mouseEvent -> mouseEvent.getButton() == MouseButton.SECONDARY,
|
||||
keyEvent -> keyEvent.getCode() == KeyCode.SPACE,
|
||||
() -> createContextMenu(name)));
|
||||
categoryButton.apply(struc -> {
|
||||
struc.get().addEventFilter(KeyEvent.KEY_PRESSED, event -> {
|
||||
if (event.getCode() == KeyCode.SPACE) {
|
||||
category.toggleExpanded();
|
||||
event.consume();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var l = category.getChildren()
|
||||
.sorted(Comparator.comparing(storeCategoryWrapper ->
|
||||
storeCategoryWrapper.nameProperty().getValue().toLowerCase(Locale.ROOT)));
|
||||
var children =
|
||||
new ListBoxViewComp<>(l, l, storeCategoryWrapper -> new StoreCategoryComp(storeCategoryWrapper), false);
|
||||
children.styleClass("children");
|
||||
|
||||
var hide = Bindings.createBooleanBinding(() -> {
|
||||
return !category.getExpanded().get() || category.getChildren().isEmpty();
|
||||
|
@ -128,6 +162,10 @@ public class StoreCategoryComp extends SimpleComp {
|
|||
StoreViewState.get().getActiveCategory().subscribe(val -> {
|
||||
struc.get().pseudoClassStateChanged(SELECTED, val.equals(category));
|
||||
});
|
||||
|
||||
category.getColor().subscribe((c) -> {
|
||||
DataColor.applyStyleClasses(c, struc.get());
|
||||
});
|
||||
});
|
||||
|
||||
return v.createRegion();
|
||||
|
@ -135,6 +173,7 @@ public class StoreCategoryComp extends SimpleComp {
|
|||
|
||||
private ContextMenu createContextMenu(Region text) {
|
||||
var contextMenu = ContextMenuHelper.create();
|
||||
AppFont.normal(contextMenu.getStyleableNode());
|
||||
|
||||
var newCategory = new MenuItem(AppI18n.get("newCategory"), new FontIcon("mdi2p-plus-thick"));
|
||||
newCategory.setOnAction(event -> {
|
||||
|
@ -144,6 +183,25 @@ public class StoreCategoryComp extends SimpleComp {
|
|||
});
|
||||
contextMenu.getItems().add(newCategory);
|
||||
|
||||
contextMenu.getItems().add(new SeparatorMenuItem());
|
||||
|
||||
var color = new Menu(AppI18n.get("color"), new FontIcon("mdi2f-format-color-fill"));
|
||||
var none = new MenuItem("None");
|
||||
none.setOnAction(event -> {
|
||||
category.getCategory().setColor(null);
|
||||
event.consume();
|
||||
});
|
||||
color.getItems().add(none);
|
||||
Arrays.stream(DataColor.values()).forEach(dataStoreColor -> {
|
||||
MenuItem m = new MenuItem(DataStoreFormatter.capitalize(dataStoreColor.getId()));
|
||||
m.setOnAction(event -> {
|
||||
category.getCategory().setColor(dataStoreColor);
|
||||
event.consume();
|
||||
});
|
||||
color.getItems().add(m);
|
||||
});
|
||||
contextMenu.getItems().add(color);
|
||||
|
||||
if (DataStorage.get().supportsSharing() && category.getCategory().canShare()) {
|
||||
var share = new MenuItem();
|
||||
share.textProperty()
|
||||
|
@ -162,7 +220,7 @@ public class StoreCategoryComp extends SimpleComp {
|
|||
if (category.getShare().getValue()) {
|
||||
return new FontIcon("mdi2b-block-helper");
|
||||
} else {
|
||||
return new FontIcon("mdi2s-share");
|
||||
return new FontIcon("mdi2g-git");
|
||||
}
|
||||
},
|
||||
category.getShare()));
|
||||
|
@ -174,10 +232,13 @@ public class StoreCategoryComp extends SimpleComp {
|
|||
|
||||
var rename = new MenuItem(AppI18n.get("rename"), new FontIcon("mdal-edit"));
|
||||
rename.setOnAction(event -> {
|
||||
text.setDisable(false);
|
||||
text.requestFocus();
|
||||
});
|
||||
contextMenu.getItems().add(rename);
|
||||
|
||||
contextMenu.getItems().add(new SeparatorMenuItem());
|
||||
|
||||
var del = new MenuItem(AppI18n.get("remove"), new FontIcon("mdal-delete_outline"));
|
||||
del.setOnAction(event -> {
|
||||
category.delete();
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
package io.xpipe.app.storage;
|
||||
|
||||
import javafx.scene.paint.Color;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.paint.Color;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
@Getter
|
||||
public enum DataStoreColor {
|
||||
public enum DataColor {
|
||||
@JsonProperty("red")
|
||||
RED("red", "\uD83D\uDD34", Color.DARKRED),
|
||||
|
||||
|
@ -23,7 +26,7 @@ public enum DataStoreColor {
|
|||
private final String emoji;
|
||||
private final Color terminalColor;
|
||||
|
||||
DataStoreColor(String id, String emoji, Color terminalColor) {
|
||||
DataColor(String id, String emoji, Color terminalColor) {
|
||||
this.id = id;
|
||||
this.emoji = emoji;
|
||||
this.terminalColor = terminalColor;
|
||||
|
@ -38,4 +41,18 @@ public enum DataStoreColor {
|
|||
var value = terminalColor;
|
||||
return "#" + (format(value.getRed()) + format(value.getGreen()) + format(value.getBlue())).toUpperCase();
|
||||
}
|
||||
|
||||
public static void applyStyleClasses(DataColor color, Node node) {
|
||||
var newList = new ArrayList<>(node.getStyleClass());
|
||||
newList.removeIf(s -> Arrays.stream(DataColor.values())
|
||||
.anyMatch(
|
||||
dataStoreColor -> dataStoreColor.getId().equals(s)));
|
||||
newList.remove("gray");
|
||||
if (color != null) {
|
||||
newList.add(color.getId());
|
||||
} else {
|
||||
newList.add("gray");
|
||||
}
|
||||
node.getStyleClass().setAll(newList);
|
||||
}
|
||||
}
|
|
@ -172,6 +172,7 @@ public abstract class DataStorage {
|
|||
"Default",
|
||||
Instant.now(),
|
||||
Instant.now(),
|
||||
null,
|
||||
true,
|
||||
ALL_CONNECTIONS_CATEGORY_UUID,
|
||||
StoreSortMode.getDefault(),
|
||||
|
@ -693,6 +694,22 @@ public abstract class DataStorage {
|
|||
return false;
|
||||
}
|
||||
|
||||
public DataColor getEffectiveColor(DataStoreEntry entry) {
|
||||
var root = getRootForEntry(entry);
|
||||
if (root.getColor() != null) {
|
||||
return root.getColor();
|
||||
}
|
||||
|
||||
var cats = getCategoryParentHierarchy(getStoreCategoryIfPresent(entry.getCategoryUuid()).orElseThrow());
|
||||
for (DataStoreCategory cat : cats.reversed()) {
|
||||
if (cat.getColor() != null) {
|
||||
return cat.getColor();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public DataStoreEntry getRootForEntry(DataStoreEntry entry) {
|
||||
if (entry == null) {
|
||||
return null;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package io.xpipe.app.storage;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import io.xpipe.app.comp.store.StoreSortMode;
|
||||
import io.xpipe.core.util.JacksonMapper;
|
||||
|
||||
|
@ -38,12 +39,13 @@ public class DataStoreCategory extends StorageElement {
|
|||
String name,
|
||||
Instant lastUsed,
|
||||
Instant lastModified,
|
||||
DataColor color,
|
||||
boolean dirty,
|
||||
UUID parentCategory,
|
||||
StoreSortMode sortMode,
|
||||
boolean share,
|
||||
boolean expanded) {
|
||||
super(directory, uuid, name, lastUsed, lastModified, expanded, dirty);
|
||||
super(directory, uuid, name, lastUsed, lastModified, color, expanded, dirty);
|
||||
this.parentCategory = parentCategory;
|
||||
this.sortMode = sortMode;
|
||||
this.share = share;
|
||||
|
@ -56,6 +58,7 @@ public class DataStoreCategory extends StorageElement {
|
|||
name,
|
||||
Instant.now(),
|
||||
Instant.now(),
|
||||
null,
|
||||
true,
|
||||
parentCategory,
|
||||
StoreSortMode.getDefault(),
|
||||
|
@ -70,6 +73,7 @@ public class DataStoreCategory extends StorageElement {
|
|||
name,
|
||||
Instant.now(),
|
||||
Instant.now(),
|
||||
null,
|
||||
true,
|
||||
parentCategory,
|
||||
StoreSortMode.getDefault(),
|
||||
|
@ -95,8 +99,17 @@ public class DataStoreCategory extends StorageElement {
|
|||
.filter(jsonNode -> !jsonNode.isNull())
|
||||
.map(jsonNode -> UUID.fromString(jsonNode.textValue()))
|
||||
.orElse(null);
|
||||
|
||||
var color = Optional.ofNullable(json.get("color"))
|
||||
.map(node -> {
|
||||
try {
|
||||
return mapper.treeToValue(node, DataColor.class);
|
||||
} catch (JsonProcessingException e) {
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.orElse(null);
|
||||
var name = json.required("name").textValue();
|
||||
|
||||
var sortMode = Optional.ofNullable(stateJson.get("sortMode"))
|
||||
.map(JsonNode::asText)
|
||||
.flatMap(string -> StoreSortMode.fromId(string))
|
||||
|
@ -116,7 +129,7 @@ public class DataStoreCategory extends StorageElement {
|
|||
.orElse(true);
|
||||
|
||||
return Optional.of(
|
||||
new DataStoreCategory(dir, uuid, name, lastUsed, lastModified, false, parentUuid, sortMode, share, expanded));
|
||||
new DataStoreCategory(dir, uuid, name, lastUsed, lastModified, color, false, parentUuid, sortMode, share, expanded));
|
||||
}
|
||||
|
||||
public void setSortMode(StoreSortMode sortMode) {
|
||||
|
@ -180,6 +193,7 @@ public class DataStoreCategory extends StorageElement {
|
|||
obj.put("uuid", uuid.toString());
|
||||
obj.put("name", name);
|
||||
obj.put("share", share);
|
||||
obj.set("color", mapper.valueToTree(color));
|
||||
stateObj.put("lastUsed", lastUsed.toString());
|
||||
stateObj.put("lastModified", lastModified.toString());
|
||||
stateObj.put("sortMode", sortMode.getId());
|
||||
|
|
|
@ -59,9 +59,6 @@ public class DataStoreEntry extends StorageElement {
|
|||
@NonFinal
|
||||
JsonNode storePersistentStateNode;
|
||||
|
||||
@NonFinal
|
||||
DataStoreColor color;
|
||||
|
||||
@NonFinal
|
||||
@Setter
|
||||
Set<DataStoreEntry> childrenCache = null;
|
||||
|
@ -86,16 +83,15 @@ public class DataStoreEntry extends StorageElement {
|
|||
Configuration configuration,
|
||||
JsonNode storePersistentState,
|
||||
boolean expanded,
|
||||
DataStoreColor color,
|
||||
DataColor color,
|
||||
String notes,
|
||||
Order explicitOrder) {
|
||||
super(directory, uuid, name, lastUsed, lastModified, expanded, dirty);
|
||||
super(directory, uuid, name, lastUsed, lastModified, color, expanded, dirty);
|
||||
this.categoryUuid = categoryUuid;
|
||||
this.store = store;
|
||||
this.storeNode = storeNode;
|
||||
this.validity = validity;
|
||||
this.configuration = configuration;
|
||||
this.color = color;
|
||||
this.explicitOrder = explicitOrder;
|
||||
this.provider = store != null ? DataStoreProviders.byStore(store) : null;
|
||||
this.storePersistentStateNode = storePersistentState;
|
||||
|
@ -111,7 +107,7 @@ public class DataStoreEntry extends StorageElement {
|
|||
Instant lastModified,
|
||||
DataStore store,
|
||||
Order explicitOrder) {
|
||||
super(directory, uuid, name, lastUsed, lastModified, false,false);
|
||||
super(directory, uuid, name, lastUsed, lastModified, null, false,false);
|
||||
this.categoryUuid = categoryUuid;
|
||||
this.store = store;
|
||||
this.explicitOrder = explicitOrder;
|
||||
|
@ -119,7 +115,6 @@ public class DataStoreEntry extends StorageElement {
|
|||
this.validity = Validity.INCOMPLETE;
|
||||
this.configuration = Configuration.defaultConfiguration();
|
||||
this.expanded = false;
|
||||
this.color = null;
|
||||
this.provider = null;
|
||||
this.storePersistentStateNode = null;
|
||||
}
|
||||
|
@ -225,7 +220,7 @@ public class DataStoreEntry extends StorageElement {
|
|||
var color = Optional.ofNullable(stateJson.get("color"))
|
||||
.map(node -> {
|
||||
try {
|
||||
return mapper.treeToValue(node, DataStoreColor.class);
|
||||
return mapper.treeToValue(node, DataColor.class);
|
||||
} catch (JsonProcessingException e) {
|
||||
return null;
|
||||
}
|
||||
|
@ -372,9 +367,9 @@ public class DataStoreEntry extends StorageElement {
|
|||
obj.put("uuid", uuid.toString());
|
||||
obj.put("name", name);
|
||||
obj.put("categoryUuid", categoryUuid.toString());
|
||||
obj.set("color", mapper.valueToTree(color));
|
||||
stateObj.put("lastUsed", lastUsed.toString());
|
||||
stateObj.put("lastModified", lastModified.toString());
|
||||
stateObj.set("color", mapper.valueToTree(color));
|
||||
stateObj.set("persistentState", storePersistentStateNode);
|
||||
obj.set("configuration", mapper.valueToTree(configuration));
|
||||
stateObj.put("expanded", expanded);
|
||||
|
@ -405,14 +400,6 @@ public class DataStoreEntry extends StorageElement {
|
|||
}
|
||||
}
|
||||
|
||||
public void setColor(DataStoreColor newColor) {
|
||||
var changed = !Objects.equals(color, newColor);
|
||||
this.color = newColor;
|
||||
if (changed) {
|
||||
notifyUpdate(false, true);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isDisabled() {
|
||||
return validity == Validity.LOAD_FAILED;
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ public class ImpersistentStorage extends DataStorage {
|
|||
"Default",
|
||||
Instant.now(),
|
||||
Instant.now(),
|
||||
null,
|
||||
true,
|
||||
ALL_CONNECTIONS_CATEGORY_UUID,
|
||||
StoreSortMode.getDefault(),
|
||||
|
|
|
@ -223,7 +223,7 @@ public class StandardStorage extends DataStorage {
|
|||
|
||||
var local = DataStorage.get().getStoreEntry(LOCAL_ID);
|
||||
if (storeEntriesSet.stream().noneMatch(entry -> entry.getColor() != null)) {
|
||||
local.setColor(DataStoreColor.BLUE);
|
||||
local.setColor(DataColor.BLUE);
|
||||
}
|
||||
|
||||
// Reload stores, this time with all entry refs present
|
||||
|
|
|
@ -13,6 +13,7 @@ import java.nio.file.Path;
|
|||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
public abstract class StorageElement {
|
||||
|
@ -42,13 +43,18 @@ public abstract class StorageElement {
|
|||
@Getter
|
||||
protected boolean expanded;
|
||||
|
||||
protected @NonFinal
|
||||
@Getter DataColor color;
|
||||
|
||||
|
||||
public StorageElement(
|
||||
Path directory, UUID uuid, String name, Instant lastUsed, Instant lastModified, boolean expanded, boolean dirty) {
|
||||
Path directory, UUID uuid, String name, Instant lastUsed, Instant lastModified, DataColor color, boolean expanded, boolean dirty) {
|
||||
this.directory = directory;
|
||||
this.uuid = uuid;
|
||||
this.name = name;
|
||||
this.lastUsed = lastUsed;
|
||||
this.lastModified = lastModified;
|
||||
this.color = color;
|
||||
this.expanded = expanded;
|
||||
this.dirty = dirty;
|
||||
}
|
||||
|
@ -83,6 +89,14 @@ public abstract class StorageElement {
|
|||
FileUtils.deleteDirectory(directory.toFile());
|
||||
}
|
||||
|
||||
public void setColor(DataColor newColor) {
|
||||
var changed = !Objects.equals(color, newColor);
|
||||
this.color = newColor;
|
||||
if (changed) {
|
||||
notifyUpdate(false, true);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void writeDataToDisk() throws Exception;
|
||||
|
||||
public synchronized Instant getLastAccess() {
|
||||
|
|
|
@ -7,7 +7,7 @@ import io.xpipe.app.core.window.AppWindowHelper;
|
|||
import io.xpipe.app.ext.PrefsChoiceValue;
|
||||
import io.xpipe.app.issue.ErrorEvent;
|
||||
import io.xpipe.app.prefs.ExternalApplicationType;
|
||||
import io.xpipe.app.storage.DataStoreColor;
|
||||
import io.xpipe.app.storage.DataColor;
|
||||
import io.xpipe.app.util.*;
|
||||
import io.xpipe.core.process.*;
|
||||
import io.xpipe.core.store.FilePath;
|
||||
|
@ -1103,7 +1103,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
|||
|
||||
@Value
|
||||
class LaunchConfiguration {
|
||||
DataStoreColor color;
|
||||
DataColor color;
|
||||
String coloredTitle;
|
||||
String cleanTitle;
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ public class TerminalLauncher {
|
|||
throw ErrorEvent.expected(new IllegalStateException(AppI18n.get("noTerminalSet")));
|
||||
}
|
||||
|
||||
var color = entry != null ? DataStorage.get().getRootForEntry(entry).getColor() : null;
|
||||
var color = entry != null ? DataStorage.get().getEffectiveColor(entry) : null;
|
||||
var prefix = entry != null && color != null && type.supportsColoredTitle() ? color.getEmoji() + " " : "";
|
||||
var cleanTitle = (title != null ? title : entry != null ? entry.getName() : "?");
|
||||
var adjustedTitle = prefix + cleanTitle;
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
.category-button {
|
||||
-fx-opacity: 0.8;
|
||||
-fx-border-width: 0;
|
||||
-fx-background-color: transparent;
|
||||
-fx-background-radius: 4px;
|
||||
-fx-border-radius: 4px;
|
||||
-fx-border-width: 1px;
|
||||
-fx-padding: 0 0 0 2;
|
||||
-fx-background-insets: 0;
|
||||
}
|
||||
|
@ -10,31 +11,43 @@
|
|||
-fx-background-color: transparent;
|
||||
}
|
||||
|
||||
.category-button .settings {
|
||||
-fx-opacity: 1.0;
|
||||
}
|
||||
|
||||
.category-button:hover, .root:key-navigation .category-button:focused {
|
||||
-fx-background-color: -color-bg-default;
|
||||
}
|
||||
|
||||
.category:selected .category-button {
|
||||
-fx-opacity: 1.0;
|
||||
-fx-background-radius: 4px;
|
||||
-fx-border-radius: 4px;
|
||||
-fx-border-width: 1px;
|
||||
-fx-border-color: -color-border-default;
|
||||
-fx-background-color: -color-bg-default;
|
||||
}
|
||||
|
||||
.category .separator {
|
||||
-fx-padding: 0 5 0 5;
|
||||
.root:light .category.yellow > .category-button .expand-button .ikonli-font-icon {
|
||||
-fx-icon-color: #888800;
|
||||
}
|
||||
|
||||
|
||||
.category .separator .line {
|
||||
-fx-pref-height: 1;
|
||||
-fx-background-color: -color-fg-default;
|
||||
-fx-opacity: 0.5;
|
||||
.root:light .category.green > .category-button .expand-button .ikonli-font-icon {
|
||||
-fx-icon-color: #0d770d;
|
||||
}
|
||||
|
||||
.root:light .category.blue > .category-button .expand-button .ikonli-font-icon {
|
||||
-fx-icon-color: #1c62be;
|
||||
}
|
||||
|
||||
.root:light .category.red > .category-button .expand-button .ikonli-font-icon {
|
||||
-fx-icon-color: #a40000;
|
||||
}
|
||||
|
||||
.root:dark .category.yellow > .category-button .expand-button .ikonli-font-icon {
|
||||
-fx-icon-color: yellow;
|
||||
}
|
||||
|
||||
.root:dark .category.green > .category-button .expand-button .ikonli-font-icon {
|
||||
-fx-icon-color: green;
|
||||
}
|
||||
|
||||
.root:dark .category.blue > .category-button .expand-button .ikonli-font-icon {
|
||||
-fx-icon-color: #397fd5;
|
||||
}
|
||||
|
||||
.root:dark .category.red > .category-button .expand-button .ikonli-font-icon {
|
||||
-fx-icon-color: red;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue