This commit is contained in:
crschnick
2025-06-28 05:45:07 +00:00
parent ce619cd831
commit 169f844966
6 changed files with 91 additions and 17 deletions

View File

@@ -454,7 +454,7 @@ public abstract class StoreEntryComp extends SimpleComp {
}
{
var order = new Menu(AppI18n.get("order"), new FontIcon("mdi2b-bookmark-multiple-outline"));
var order = new Menu(AppI18n.get("order"), new FontIcon("mdi2f-format-list-bulleted"));
var index = new MenuItem(AppI18n.get("index"), new FontIcon("mdi2o-order-numeric-ascending"));
index.setOnAction(event -> {
@@ -475,14 +475,14 @@ public abstract class StoreEntryComp extends SimpleComp {
order.getItems().add(noOrder);
}
var first = new MenuItem(AppI18n.get("moveToTop"), new FontIcon("mdi2o-order-bool-descending"));
var first = new MenuItem(AppI18n.get("moveToFirst"), new FontIcon("mdi2o-order-bool-descending"));
first.setOnAction(event -> {
getWrapper().orderFirst();
event.consume();
});
order.getItems().add(first);
var last = new MenuItem(AppI18n.get("moveToBottom"), new FontIcon("mdi2o-order-bool-ascending"));
var last = new MenuItem(AppI18n.get("moveToLast"), new FontIcon("mdi2o-order-bool-ascending"));
last.setOnAction(event -> {
getWrapper().orderLast();
event.consume();
@@ -492,7 +492,7 @@ public abstract class StoreEntryComp extends SimpleComp {
order.getItems().add(new SeparatorMenuItem());
var top = new MenuItem(
AppI18n.get("stickToTop"), new FontIcon("mdi2o-order-bool-descending-variant"));
AppI18n.get("keepFirst"), new FontIcon("mdi2o-order-bool-descending-variant"));
top.setOnAction(event -> {
getWrapper().orderStickFirst();
event.consume();
@@ -501,7 +501,7 @@ public abstract class StoreEntryComp extends SimpleComp {
order.getItems().add(top);
var bottom = new MenuItem(
AppI18n.get("stickToBottom"), new FontIcon("mdi2o-order-bool-ascending-variant"));
AppI18n.get("keepLast"), new FontIcon("mdi2o-order-bool-ascending-variant"));
bottom.setOnAction(event -> {
getWrapper().orderStickLast();
event.consume();
@@ -538,6 +538,32 @@ public abstract class StoreEntryComp extends SimpleComp {
});
items.add(move);
}
if (getWrapper().getPinToTop().getValue() || section.getDepth() > 1) {
var pinToTop = new MenuItem();
pinToTop.graphicProperty()
.bind(Bindings.createObjectBinding(
() -> {
var is = getWrapper().getPinToTop().get();
return is
? new FontIcon("mdi2p-pin-off-outline")
: new FontIcon("mdi2p-pin-outline");
},
getWrapper().getPinToTop()));
pinToTop.textProperty()
.bind(Bindings.createStringBinding(
() -> {
var is = getWrapper().getPinToTop().get();
return is
? AppI18n.get("unpinFromTop")
: AppI18n.get("pinToTop");
},
AppI18n.activeLanguage(),
getWrapper().getPinToTop()));
pinToTop.setOnAction(event -> getWrapper()
.togglePinToTop());
items.add(pinToTop);
}
}
if (cat == StoreActionCategory.DELETION) {

View File

@@ -64,6 +64,7 @@ public class StoreEntryWrapper {
private final BooleanProperty largeCategoryOptimizations = new SimpleBooleanProperty();
private final BooleanProperty readOnly = new SimpleBooleanProperty();
private final BooleanProperty renaming = new SimpleBooleanProperty();
private final BooleanProperty pinToTop = new SimpleBooleanProperty();
private final IntegerProperty orderIndex = new SimpleIntegerProperty();
private boolean effectiveBusyProviderBound = false;
@@ -197,6 +198,7 @@ public class StoreEntryWrapper {
perUser.setValue(
!category.getValue().getRoot().equals(StoreViewState.get().getAllIdentitiesCategory())
&& entry.isPerUserStore());
pinToTop.setValue(entry.isPinToTop());
var storeChanged = store.getValue() != entry.getStore();
store.setValue(entry.getStore());
@@ -416,6 +418,23 @@ public class StoreEntryWrapper {
this.expanded.set(!expanded.getValue());
}
public void togglePinToTop() {
if (getEntry().isPinToTop()) {
getEntry().setPinToTop(false);
StoreViewState.get().triggerStoreListUpdate();
} else {
var root = StoreViewState.get().getCurrentTopLevelSection().getAllChildren().getList().stream().filter(
storeSection -> storeSection.anyMatches(storeEntryWrapper -> storeEntryWrapper == this)).findFirst();
var sortMode = StoreSectionSortMode.DATE_DESC;
var date = root.isPresent() ? sortMode.date(sortMode.getRepresentative(root.get())).plus(Duration.ofSeconds(1)) : Instant.now();
getEntry().setPinToTop(!getEntry().isPinToTop());
StoreViewState.get().triggerStoreListUpdate();
getEntry().setLastUsed(date);
getEntry().setLastModified(date);
StoreViewState.get().triggerStoreListUpdate();
}
}
public boolean matchesFilter(String filter) {
if (filter == null || name.getValue().toLowerCase().contains(filter.toLowerCase())) {
return true;

View File

@@ -57,9 +57,9 @@ public interface StoreSectionSortMode {
.reversed();
}
};
StoreSectionSortMode DATE_DESC = new StoreSectionSortMode.DateSortMode() {
StoreSectionSortMode.DateSortMode DATE_DESC = new StoreSectionSortMode.DateSortMode() {
protected Instant date(StoreSection s) {
public Instant date(StoreSection s) {
var la = s.getWrapper().getLastAccess().getValue();
if (la == null) {
return Instant.MAX;
@@ -78,9 +78,9 @@ public interface StoreSectionSortMode {
return "date-desc";
}
};
StoreSectionSortMode DATE_ASC = new StoreSectionSortMode.DateSortMode() {
StoreSectionSortMode.DateSortMode DATE_ASC = new StoreSectionSortMode.DateSortMode() {
protected Instant date(StoreSection s) {
public Instant date(StoreSection s) {
var la = s.getWrapper().getLastAccess().getValue();
if (la == null) {
return Instant.MIN;
@@ -117,7 +117,7 @@ public interface StoreSectionSortMode {
private int entriesListObservableIndex = -1;
private final Map<StoreSection, StoreSection> cachedRepresentatives = new IdentityHashMap<>();
private StoreSection computeRepresentative(StoreSection s) {
public StoreSection computeRepresentative(StoreSection s) {
return Stream.concat(
s.getShownChildren().getList().stream()
.filter(section -> section.getWrapper()
@@ -130,7 +130,7 @@ public interface StoreSectionSortMode {
.orElseThrow();
}
private StoreSection getRepresentative(StoreSection s) {
public StoreSection getRepresentative(StoreSection s) {
if (StoreViewState.get().getEntriesListUpdateObservable().get() != entriesListObservableIndex) {
cachedRepresentatives.clear();
entriesListObservableIndex =
@@ -146,7 +146,7 @@ public interface StoreSectionSortMode {
return r;
}
protected abstract Instant date(StoreSection s);
public abstract Instant date(StoreSection s);
protected abstract int compare(Instant s1, Instant s2);

View File

@@ -862,6 +862,10 @@ public abstract class DataStorage {
// Get operations
public boolean isRootEntry(DataStoreEntry entry, DataStoreCategory current) {
if (entry.isPinToTop()) {
return true;
}
var parent = getDefaultDisplayParent(entry);
var noParent = parent.isEmpty();
if (noParent) {

View File

@@ -79,6 +79,10 @@ public class DataStoreEntry extends StorageElement {
@Getter
boolean freeze;
@NonFinal
@Getter
boolean pinToTop;
@Getter
@NonFinal
int orderIndex;
@@ -100,6 +104,7 @@ public class DataStoreEntry extends StorageElement {
String notes,
String icon,
boolean freeze,
boolean pinToTop,
int orderIndex) {
super(directory, uuid, name, lastUsed, lastModified, expanded, dirty);
this.color = color;
@@ -112,6 +117,7 @@ public class DataStoreEntry extends StorageElement {
this.notes = notes;
this.icon = icon;
this.freeze = freeze;
this.pinToTop = pinToTop;
this.orderIndex = orderIndex;
}
@@ -133,6 +139,7 @@ public class DataStoreEntry extends StorageElement {
null,
null,
false,
false,
0);
}
@@ -171,6 +178,7 @@ public class DataStoreEntry extends StorageElement {
null,
null,
false,
false,
0);
return entry;
}
@@ -228,6 +236,9 @@ public class DataStoreEntry extends StorageElement {
var freeze = Optional.ofNullable(json.get("freeze"))
.map(jsonNode -> jsonNode.booleanValue())
.orElse(false);
var pinToTop = Optional.ofNullable(json.get("pinToTop"))
.map(jsonNode -> jsonNode.booleanValue())
.orElse(false);
var iconNode = json.get("icon");
String icon = iconNode != null && !iconNode.isNull() ? iconNode.asText() : null;
@@ -305,6 +316,7 @@ public class DataStoreEntry extends StorageElement {
notes,
icon,
freeze,
pinToTop,
orderIndex));
}
@@ -464,6 +476,7 @@ public class DataStoreEntry extends StorageElement {
obj.set("color", mapper.valueToTree(color));
obj.set("icon", mapper.valueToTree(icon));
obj.put("freeze", freeze);
obj.put("pinToTop", pinToTop);
obj.put("orderIndex", orderIndex);
ObjectNode stateObj = JsonNodeFactory.instance.objectNode();
@@ -519,6 +532,15 @@ public class DataStoreEntry extends StorageElement {
}
}
public void setPinToTop(boolean newValue) {
var changed = pinToTop != newValue;
this.pinToTop = newValue;
if (changed) {
notifyUpdate(false, false);
dirty = true;
}
}
public boolean isDisabled() {
return validity == Validity.LOAD_FAILED;
}

View File

@@ -442,9 +442,12 @@ skipAll=Skip all
notes=Notes
addNotes=Add notes
#context: verb, to reorder
order=Order ...
stickToTop=Keep on top
stickToBottom=Keep on bottom
#force
order=Order
keepFirst=Keep first
keepLast=Keep last
pinToTop=Pin to top
unpinFromTop=Unpin from top
orderAheadOf=Order ahead of ...
httpServer=HTTP server
httpServerConfiguration=HTTP server configuration
@@ -1525,8 +1528,8 @@ appleContainers=Apple containers
changeOrderIndexTitle=Change order index
orderIndex=Index
orderIndexDescription=Explicit index to order this entry relative to others. Lowest indices are shown on top, highest on the bottom
moveToBottom=Move to bottom
moveToTop=Move to top
moveToFirst=Move to first
moveToLast=Move to last
category=Category
includeRoot=Include root
excludeRoot=Exclude root