|
10 | 10 | import com.ss.editor.manager.ExecutorManager; |
11 | 11 | import com.ss.editor.ui.FXConstants; |
12 | 12 | import com.ss.editor.ui.component.asset.tree.context.menu.action.*; |
13 | | -import com.ss.editor.ui.component.asset.tree.context.menu.filler.AssetTreeContextMenuFiller; |
| 13 | +import com.ss.editor.ui.component.asset.tree.context.menu.filler.AssetTreeMultiContextMenuFiller; |
| 14 | +import com.ss.editor.ui.component.asset.tree.context.menu.filler.AssetTreeSingleContextMenuFiller; |
14 | 15 | import com.ss.editor.ui.component.asset.tree.resource.FileResourceElement; |
15 | 16 | import com.ss.editor.ui.component.asset.tree.resource.FolderResourceElement; |
16 | 17 | import com.ss.editor.ui.component.asset.tree.resource.LoadingResourceElement; |
|
33 | 34 | import org.jetbrains.annotations.Nullable; |
34 | 35 |
|
35 | 36 | import java.nio.file.Path; |
| 37 | +import java.util.Objects; |
36 | 38 | import java.util.function.Consumer; |
37 | 39 | import java.util.function.Predicate; |
38 | 40 |
|
@@ -172,6 +174,7 @@ public ResourceTree(@Nullable final Consumer<ResourceElement> openFunction, fina |
172 | 174 | expandedItemCountProperty() |
173 | 175 | .addListener((observable, oldValue, newValue) -> processChangedExpands(newValue)); |
174 | 176 |
|
| 177 | + getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); |
175 | 178 | setFixedCellSize(FXConstants.RESOURCE_TREE_CELL_HEIGHT); |
176 | 179 | setCellFactory(param -> new ResourceTreeCell()); |
177 | 180 | setOnKeyPressed(this::processKey); |
@@ -373,9 +376,32 @@ private boolean isReadOnly() { |
373 | 376 |
|
374 | 377 | final Predicate<Class<?>> actionTester = getActionTester(); |
375 | 378 |
|
376 | | - final Array<AssetTreeContextMenuFiller> fillers = CONTEXT_MENU_FILLER_REGISTRY.getFillers(); |
377 | | - for (final AssetTreeContextMenuFiller filler : fillers) { |
378 | | - filler.fill(element, items, actionTester); |
| 379 | + final MultipleSelectionModel<TreeItem<ResourceElement>> selectionModel = getSelectionModel(); |
| 380 | + final ObservableList<TreeItem<ResourceElement>> selectedItems = selectionModel.getSelectedItems(); |
| 381 | + |
| 382 | + if (selectedItems.size() == 1) { |
| 383 | + final Array<AssetTreeSingleContextMenuFiller> fillers = CONTEXT_MENU_FILLER_REGISTRY.getSingleFillers(); |
| 384 | + for (final AssetTreeSingleContextMenuFiller filler : fillers) { |
| 385 | + filler.fill(element, items, actionTester); |
| 386 | + } |
| 387 | + } |
| 388 | + |
| 389 | + if (selectedItems.size() >= 1) { |
| 390 | + updateSelectedElements(); |
| 391 | + |
| 392 | + final ConcurrentArray<ResourceElement> selectedElements = getSelectedElements(); |
| 393 | + |
| 394 | + final long stamp = selectedElements.readLock(); |
| 395 | + try { |
| 396 | + |
| 397 | + final Array<AssetTreeMultiContextMenuFiller> fillers = CONTEXT_MENU_FILLER_REGISTRY.getMultiFillers(); |
| 398 | + for (final AssetTreeMultiContextMenuFiller filler : fillers) { |
| 399 | + filler.fill(selectedElements, items, actionTester); |
| 400 | + } |
| 401 | + |
| 402 | + } finally { |
| 403 | + selectedElements.readUnlock(stamp); |
| 404 | + } |
379 | 405 | } |
380 | 406 |
|
381 | 407 | if (items.isEmpty()) return null; |
@@ -723,44 +749,66 @@ public void notifyRenamed(@NotNull final Path prevFile, @NotNull final Path newF |
723 | 749 | private void processKey(@NotNull final KeyEvent event) { |
724 | 750 | if (isReadOnly()) return; |
725 | 751 |
|
726 | | - final MultipleSelectionModel<TreeItem<ResourceElement>> selectionModel = getSelectionModel(); |
727 | | - final TreeItem<ResourceElement> selectedItem = selectionModel.getSelectedItem(); |
728 | | - if (selectedItem == null) return; |
729 | | - |
730 | | - final ResourceElement item = selectedItem.getValue(); |
731 | | - if (item == null || item instanceof LoadingResourceElement) return; |
732 | | - |
733 | 752 | final EditorConfig editorConfig = EditorConfig.getInstance(); |
734 | 753 | final Path currentAsset = editorConfig.getCurrentAsset(); |
735 | 754 | if (currentAsset == null) return; |
736 | 755 |
|
| 756 | + updateSelectedElements(); |
| 757 | + |
| 758 | + final ConcurrentArray<ResourceElement> selectedElements = getSelectedElements(); |
| 759 | + if (selectedElements.isEmpty()) return; |
| 760 | + |
| 761 | + final ResourceElement firstElement = selectedElements.first(); |
| 762 | + if (firstElement instanceof LoadingResourceElement) return; |
| 763 | + |
| 764 | + boolean onlyFiles = true; |
| 765 | + boolean onlyFolders = true; |
| 766 | + boolean selectedAsset = false; |
| 767 | + |
| 768 | + for (final ResourceElement element : selectedElements.array()) { |
| 769 | + if (element == null) break; |
| 770 | + |
| 771 | + if (element instanceof FileResourceElement) { |
| 772 | + onlyFolders = false; |
| 773 | + } else if (element instanceof FolderResourceElement) { |
| 774 | + onlyFiles = false; |
| 775 | + } |
| 776 | + |
| 777 | + if (Objects.equals(currentAsset, element.getFile())) { |
| 778 | + selectedAsset = true; |
| 779 | + } |
| 780 | + } |
| 781 | + |
737 | 782 | final Predicate<Class<?>> actionTester = getActionTester(); |
738 | 783 | final KeyCode keyCode = event.getCode(); |
739 | 784 | final boolean controlDown = event.isControlDown(); |
740 | 785 |
|
741 | | - if (!currentAsset.equals(item.getFile())) { |
742 | | - if (controlDown && keyCode == KeyCode.C && actionTester.test(CopyFileAction.class)) { |
| 786 | + if (!currentAsset.equals(firstElement.getFile())) { |
| 787 | + if (controlDown && keyCode == KeyCode.C && actionTester.test(CopyFileAction.class) && selectedAsset && |
| 788 | + (onlyFiles || selectedElements.size() == 1)) { |
743 | 789 |
|
744 | | - final CopyFileAction action = new CopyFileAction(item); |
| 790 | + final CopyFileAction action = new CopyFileAction(selectedElements); |
745 | 791 | final EventHandler<ActionEvent> onAction = action.getOnAction(); |
746 | 792 | onAction.handle(null); |
747 | 793 |
|
748 | | - } else if (controlDown && keyCode == KeyCode.X && actionTester.test(CutFileAction.class)) { |
| 794 | + } else if (controlDown && keyCode == KeyCode.X && actionTester.test(CutFileAction.class) && selectedAsset && |
| 795 | + (onlyFiles || selectedElements.size() == 1)) { |
749 | 796 |
|
750 | | - final CutFileAction action = new CutFileAction(item); |
| 797 | + final CutFileAction action = new CutFileAction(selectedElements); |
751 | 798 | final EventHandler<ActionEvent> onAction = action.getOnAction(); |
752 | 799 | onAction.handle(null); |
753 | 800 |
|
754 | | - } else if (keyCode == KeyCode.DELETE && actionTester.test(DeleteFileAction.class)) { |
| 801 | + } else if (keyCode == KeyCode.DELETE && actionTester.test(DeleteFileAction.class) && selectedAsset && |
| 802 | + (onlyFiles || selectedElements.size() == 1)) { |
755 | 803 |
|
756 | | - final DeleteFileAction action = new DeleteFileAction(item); |
| 804 | + final DeleteFileAction action = new DeleteFileAction(selectedElements); |
757 | 805 | final EventHandler<ActionEvent> onAction = action.getOnAction(); |
758 | 806 | onAction.handle(null); |
759 | 807 | } |
760 | 808 | } |
761 | 809 |
|
762 | 810 | if (controlDown && keyCode == KeyCode.V && hasFileInClipboard() && actionTester.test(PasteFileAction.class)) { |
763 | | - final PasteFileAction action = new PasteFileAction(item); |
| 811 | + final PasteFileAction action = new PasteFileAction(firstElement); |
764 | 812 | final EventHandler<ActionEvent> onAction = action.getOnAction(); |
765 | 813 | onAction.handle(null); |
766 | 814 | } |
|
0 commit comments