|
30 | 30 | import dev.ikm.komet.kview.events.pattern.MakePatternWindowEvent; |
31 | 31 | import dev.ikm.komet.kview.mvvm.view.AbstractBasicController; |
32 | 32 | import dev.ikm.komet.layout.editor.StandardEditorWindows; |
| 33 | +import dev.ikm.tinkar.common.id.PublicIds; |
| 34 | +import dev.ikm.tinkar.coordinate.stamp.calculator.Latest; |
33 | 35 | import dev.ikm.tinkar.coordinate.stamp.calculator.LatestVersionSearchResult; |
34 | 36 | import dev.ikm.tinkar.entity.ConceptEntity; |
35 | 37 | import dev.ikm.tinkar.entity.Entity; |
36 | 38 | import dev.ikm.tinkar.entity.EntityService; |
37 | 39 | import dev.ikm.tinkar.entity.EntityVersion; |
38 | 40 | import dev.ikm.tinkar.entity.PatternEntity; |
| 41 | +import dev.ikm.tinkar.entity.SemanticEntityVersion; |
39 | 42 | import dev.ikm.tinkar.events.EvtBus; |
40 | 43 | import dev.ikm.tinkar.events.EvtBusFactory; |
41 | 44 | import dev.ikm.tinkar.common.service.RemoteConceptSearchService; |
@@ -87,7 +90,7 @@ public class SortResultConceptEntryController extends AbstractBasicController { |
87 | 90 | private Label retiredLabel; |
88 | 91 |
|
89 | 92 | @FXML |
90 | | - private ListView<LatestVersionSearchResult> descriptionsListView; |
| 93 | + private ListView<DescriptionSemanticRow> descriptionsListView; |
91 | 94 |
|
92 | 95 | @FXML |
93 | 96 | private Node dragIndicator; |
@@ -143,7 +146,7 @@ public void initialize() { |
143 | 146 | }); |
144 | 147 |
|
145 | 148 | descriptionsListView.setFixedCellSize(LIST_VIEW_CELL_SIZE); |
146 | | - descriptionsListView.getItems().addListener((ListChangeListener<? super LatestVersionSearchResult>) change -> updateListViewPrefHeight()); |
| 149 | + descriptionsListView.getItems().addListener((ListChangeListener<? super DescriptionSemanticRow>) change -> updateListViewPrefHeight()); |
147 | 150 | updateListViewPrefHeight(); |
148 | 151 |
|
149 | 152 | descriptionsListView.setCellFactory(param -> new DescriptionSemanticListCell()); |
@@ -203,7 +206,24 @@ public void setComponentText(String highlightedTopText) { |
203 | 206 | HighlightedSegments.renderHighlightedInto(componentTextFlow, highlightedTopText); |
204 | 207 | } |
205 | 208 |
|
206 | | - public ObservableList<LatestVersionSearchResult> getDescriptionListViewItems() { return descriptionsListView.getItems(); } |
| 209 | + public ObservableList<DescriptionSemanticRow> getDescriptionListViewItems() { return descriptionsListView.getItems(); } |
| 210 | + |
| 211 | + /** |
| 212 | + * One row of the expanded description-semantics list. |
| 213 | + * |
| 214 | + * <p>{@code publicIds} is populated only for rows from a remote search service, whose |
| 215 | + * {@code result} carries an absent {@link Latest} because there is no local entity |
| 216 | + * behind it. It is the row's only identity in that case; locally sourced rows leave it |
| 217 | + * empty and resolve identity from the result's own version. |
| 218 | + * |
| 219 | + * @param result the search result backing this row |
| 220 | + * @param publicIds UUIDs of the matched semantic, for remote rows |
| 221 | + */ |
| 222 | + public record DescriptionSemanticRow(LatestVersionSearchResult result, List<UUID> publicIds) { |
| 223 | + public DescriptionSemanticRow(LatestVersionSearchResult result) { |
| 224 | + this(result, List.of()); |
| 225 | + } |
| 226 | + } |
207 | 227 |
|
208 | 228 | @Override |
209 | 229 | public void updateView() { |
@@ -288,7 +308,7 @@ private void updateListViewPrefHeight() { |
288 | 308 | * * |
289 | 309 | **************************************************************************/ |
290 | 310 |
|
291 | | - class DescriptionSemanticListCell extends ListCell<LatestVersionSearchResult> { |
| 311 | + class DescriptionSemanticListCell extends ListCell<DescriptionSemanticRow> { |
292 | 312 | private HBox cellContainer = new HBox(); |
293 | 313 | private TextFlow textFlow = new TextFlow(); |
294 | 314 | private ImageView identicon = new ImageView(); |
@@ -326,17 +346,32 @@ public DescriptionSemanticListCell() { |
326 | 346 | } |
327 | 347 |
|
328 | 348 | @Override |
329 | | - protected void updateItem(LatestVersionSearchResult item, boolean empty) { |
| 349 | + protected void updateItem(DescriptionSemanticRow item, boolean empty) { |
330 | 350 | if (item == null || empty) { |
331 | 351 | setGraphic(null); |
| 352 | + identicon.setImage(null); |
332 | 353 | currentNid = -1; |
333 | 354 | } else { |
334 | | - item.latestVersion().ifPresent(semanticEntityVersion -> { |
| 355 | + // Reset first: cells are recycled, and a remote row resolves neither the |
| 356 | + // image nor the nid, so a stale one would otherwise show the previous |
| 357 | + // row's icon and open the previous row's semantic on double-click. |
| 358 | + identicon.setImage(null); |
| 359 | + currentNid = -1; |
| 360 | + |
| 361 | + LatestVersionSearchResult result = item.result(); |
| 362 | + if (result.latestVersion() != null && result.latestVersion().isPresent()) { |
| 363 | + SemanticEntityVersion semanticEntityVersion = result.latestVersion().get(); |
335 | 364 | identicon.setImage(Identicon.generateIdenticonImage(semanticEntityVersion.publicId())); |
336 | 365 | currentNid = semanticEntityVersion.nid(); |
337 | 366 | setUpDraggable(cellContainer, semanticEntityVersion.entity(), getDragAndDropType(semanticEntityVersion.entity())); |
338 | | - }); |
339 | | - HighlightedSegments.renderHighlightedInto(textFlow, item.highlightedString()); |
| 367 | + } else if (!item.publicIds().isEmpty()) { |
| 368 | + // Remote row: no local entity, so the public ID sent with the match is |
| 369 | + // the only identity available. Enough to draw the icon; opening and |
| 370 | + // dragging still need a real entity and stay disabled. |
| 371 | + identicon.setImage(Identicon.generateIdenticonImage( |
| 372 | + PublicIds.of(item.publicIds().toArray(new UUID[0])))); |
| 373 | + } |
| 374 | + HighlightedSegments.renderHighlightedInto(textFlow, result.highlightedString()); |
340 | 375 | setGraphic(cellContainer); |
341 | 376 | } |
342 | 377 | } |
|
0 commit comments