Rework categories

This commit is contained in:
crschnick 2024-08-22 17:34:36 +00:00
parent a65a0bd1b0
commit be684d7b72
19 changed files with 228 additions and 109 deletions

View file

@ -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());
}

View file

@ -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

View file

@ -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 -> {

View file

@ -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);

View file

@ -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;

View file

@ -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");

View file

@ -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");

View file

@ -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);
}

View file

@ -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(
() -> {
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";
},
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()) {
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;
}));
}))
.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();

View file

@ -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);
}
}

View file

@ -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;

View file

@ -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());

View file

@ -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;
}

View file

@ -30,6 +30,7 @@ public class ImpersistentStorage extends DataStorage {
"Default",
Instant.now(),
Instant.now(),
null,
true,
ALL_CONNECTIONS_CATEGORY_UUID,
StoreSortMode.getDefault(),

View file

@ -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

View file

@ -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() {

View file

@ -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;

View file

@ -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;

View file

@ -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;
}