From 8c34441103013a4562fcda74c8b052b2acbfa95b Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Tue, 5 Sep 2017 18:39:53 +0300 Subject: [PATCH 01/50] refactoring material editors to use as plugin API. --- app.version | 2 +- build-native.xml | 2 +- build.gradle | 2 +- .../java/com/ss/editor/config/Config.java | 42 +- .../undo/editor/MaterialChangeConsumer.java | 21 - .../Advanced3DFileEditorWithRightTool.java | 30 +- .../material/BaseMaterialEditor3DState.java | 367 ++++++++++++++++ .../material/BaseMaterialFileEditor.java | 409 ++++++++++++++++++ .../impl/material/MaterialEditor3DState.java | 357 +-------------- .../impl/material/MaterialFileEditor.java | 365 +--------------- .../operation/RenderStateOperation.java | 76 ---- .../state/impl/EditorMaterialEditorState.java | 6 +- 12 files changed, 842 insertions(+), 837 deletions(-) delete mode 100644 src/main/java/com/ss/editor/model/undo/editor/MaterialChangeConsumer.java create mode 100644 src/main/java/com/ss/editor/plugin/api/editor/material/BaseMaterialEditor3DState.java create mode 100644 src/main/java/com/ss/editor/plugin/api/editor/material/BaseMaterialFileEditor.java delete mode 100644 src/main/java/com/ss/editor/ui/component/editor/impl/material/operation/RenderStateOperation.java diff --git a/app.version b/app.version index 1cc5f657..867e5243 100644 --- a/app.version +++ b/app.version @@ -1 +1 @@ -1.1.0 \ No newline at end of file +1.2.0 \ No newline at end of file diff --git a/build-native.xml b/build-native.xml index 6039a420..c402aa5c 100644 --- a/build-native.xml +++ b/build-native.xml @@ -2,7 +2,7 @@ - + editor3DArea.requestFocus()); - editor3DArea.setOnKeyReleased(Event::consume); - editor3DArea.setOnKeyPressed(Event::consume); + createEditorAreaPane(); mainSplitContainer = new EditorToolSplitPane(JFX_APPLICATION.getScene(), root); @@ -74,11 +66,27 @@ protected void createContent(@NotNull final StackPane root) { if (editorState != null) editorState.setOpenedTool(newValue.intValue()); }); - mainSplitContainer.initFor(editorToolComponent, editorAreaPane); + mainSplitContainer.initFor(editorToolComponent, getEditorAreaPane()); FXUtils.addToPane(mainSplitContainer, root); - FXUtils.addToPane(editor3DArea, editorAreaPane); FXUtils.addClassTo(mainSplitContainer, CSSClasses.FILE_EDITOR_MAIN_SPLIT_PANE); + } + + /** + * Create editor area pane. + */ + protected void createEditorAreaPane() { + + editorAreaPane = new StackPane(); + editorAreaPane.setOnDragOver(this::dragOver); + editorAreaPane.setOnDragDropped(this::dragDropped); + + editor3DArea = new BorderPane(); + editor3DArea.setOnMousePressed(event -> editor3DArea.requestFocus()); + editor3DArea.setOnKeyReleased(Event::consume); + editor3DArea.setOnKeyPressed(Event::consume); + + FXUtils.addToPane(editor3DArea, editorAreaPane); FXUtils.addClassTo(editorAreaPane, CSSClasses.FILE_EDITOR_EDITOR_AREA); } diff --git a/src/main/java/com/ss/editor/plugin/api/editor/material/BaseMaterialEditor3DState.java b/src/main/java/com/ss/editor/plugin/api/editor/material/BaseMaterialEditor3DState.java new file mode 100644 index 00000000..c4203e47 --- /dev/null +++ b/src/main/java/com/ss/editor/plugin/api/editor/material/BaseMaterialEditor3DState.java @@ -0,0 +1,367 @@ +package com.ss.editor.plugin.api.editor.material; + +import static com.ss.rlib.util.ObjectUtils.notNull; +import com.jme3.app.Application; +import com.jme3.app.state.AppStateManager; +import com.jme3.asset.AssetNotFoundException; +import com.jme3.input.InputManager; +import com.jme3.input.KeyInput; +import com.jme3.input.controls.KeyTrigger; +import com.jme3.light.DirectionalLight; +import com.jme3.material.Material; +import com.jme3.math.Vector3f; +import com.jme3.renderer.RenderManager; +import com.jme3.renderer.RendererException; +import com.jme3.renderer.queue.RenderQueue.Bucket; +import com.jme3.scene.Geometry; +import com.jme3.scene.Node; +import com.jme3.scene.shape.Box; +import com.jme3.scene.shape.Quad; +import com.jme3.scene.shape.Sphere; +import com.ss.editor.EditorThread; +import com.ss.editor.model.EditorCamera; +import com.ss.editor.model.tool.TangentGenerator; +import com.ss.editor.plugin.api.editor.part3d.AdvancedPBRWithStudioSky3DEditorState; +import com.ss.editor.ui.component.editor.impl.material.MaterialFileEditor; +import com.ss.editor.util.EditorUtil; +import com.ss.rlib.function.BooleanFloatConsumer; +import com.ss.rlib.geom.util.AngleUtils; +import com.ss.rlib.util.dictionary.ObjectDictionary; +import javafx.scene.input.KeyCode; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * The implementation the 3D part of the {@link MaterialFileEditor}. + * + * @author JavaSaBr + */ +public class BaseMaterialEditor3DState extends AdvancedPBRWithStudioSky3DEditorState { + + @NotNull + private static final Vector3f QUAD_OFFSET = new Vector3f(0, -2, 2); + + @NotNull + private static final Vector3f LIGHT_DIRECTION = new Vector3f(0.007654993F, 0.39636374F, 0.9180617F).negate(); + + private static final float H_ROTATION = AngleUtils.degreeToRadians(75); + private static final float V_ROTATION = AngleUtils.degreeToRadians(25); + + @NotNull + private static final String KEY_C = "SSEditor.materialEditorState.C"; + + @NotNull + private static final String KEY_S = "SSEditor.materialEditorState.S"; + + @NotNull + private static final String KEY_P = "SSEditor.materialEditorState.P"; + + @NotNull + private static final String KEY_L = "SSEditor.materialEditorState.L"; + + static { + TRIGGERS.put(KEY_C, new KeyTrigger(KeyInput.KEY_C)); + TRIGGERS.put(KEY_S, new KeyTrigger(KeyInput.KEY_S)); + TRIGGERS.put(KEY_P, new KeyTrigger(KeyInput.KEY_P)); + TRIGGERS.put(KEY_L, new KeyTrigger(KeyInput.KEY_L)); + } + + /** + * The test box. + */ + @NotNull + private final Geometry testBox; + + /** + * The test sphere. + */ + @NotNull + private final Geometry testSphere; + + /** + * The test quad. + */ + @NotNull + private final Geometry testQuad; + + /** + * The current model mode. + */ + @Nullable + private ModelType currentModelType; + + /** + * The flag of enabling light. + */ + private boolean lightEnabled; + + /** + * Instantiates a new Material editor app state. + * + * @param fileEditor the file editor + */ + public BaseMaterialEditor3DState(@NotNull final T fileEditor) { + super(fileEditor); + this.testBox = new Geometry("Box", new Box(2, 2, 2)); + this.testSphere = new Geometry("Sphere", new Sphere(30, 30, 2)); + this.testQuad = new Geometry("Quad", new Quad(4, 4)); + this.testQuad.setLocalTranslation(QUAD_OFFSET); + this.lightEnabled = MaterialFileEditor.DEFAULT_LIGHT_ENABLED; + + TangentGenerator.useMikktspaceGenerator(testBox); + TangentGenerator.useMikktspaceGenerator(testSphere); + TangentGenerator.useMikktspaceGenerator(testQuad); + + final DirectionalLight light = notNull(getLightForCamera()); + light.setDirection(LIGHT_DIRECTION); + + final EditorCamera editorCamera = notNull(getEditorCamera()); + editorCamera.setDefaultHorizontalRotation(H_ROTATION); + editorCamera.setDefaultVerticalRotation(V_ROTATION); + + getModelNode().attachChild(getNodeForCamera()); + } + + @Override + protected void registerActionHandlers(@NotNull final ObjectDictionary actionHandlers) { + super.registerActionHandlers(actionHandlers); + + final T fileEditor = getFileEditor(); + + actionHandlers.put(KEY_S, (isPressed, tpf) -> fileEditor.handleKeyAction(KeyCode.S, isPressed, isControlDown(), isButtonMiddleDown())); + actionHandlers.put(KEY_C, (isPressed, tpf) -> fileEditor.handleKeyAction(KeyCode.C, isPressed, isControlDown(), isButtonMiddleDown())); + actionHandlers.put(KEY_P, (isPressed, tpf) -> fileEditor.handleKeyAction(KeyCode.P, isPressed, isControlDown(), isButtonMiddleDown())); + actionHandlers.put(KEY_L, (isPressed, tpf) -> fileEditor.handleKeyAction(KeyCode.L, isPressed, isControlDown(), isButtonMiddleDown())); + } + + @Override + protected void registerActionListener(@NotNull final InputManager inputManager) { + super.registerActionListener(inputManager); + inputManager.addListener(actionListener, KEY_S, KEY_C, KEY_P, KEY_L); + } + + /** + * @return the test box. + */ + protected @NotNull Geometry getTestBox() { + return testBox; + } + + /** + * @return the test quad. + */ + protected @NotNull Geometry getTestQuad() { + return testQuad; + } + + /** + * @return the test sphere. + */ + protected @NotNull Geometry getTestSphere() { + return testSphere; + } + + /** + * Update the {@link Material}. + * + * @param material the material + */ + public void updateMaterial(@NotNull final Material material) { + EXECUTOR_MANAGER.addJMETask(() -> updateMaterialImpl(material)); + } + + /** + * Update the {@link Material} in the {@link EditorThread}. + */ + protected void updateMaterialImpl(@NotNull final Material material) { + + final Geometry testBox = getTestBox(); + testBox.setMaterial(material); + + final Geometry testQuad = getTestQuad(); + testQuad.setMaterial(material); + + final Geometry testSphere = getTestSphere(); + testSphere.setMaterial(material); + + final RenderManager renderManager = EDITOR.getRenderManager(); + try { + renderManager.preloadScene(testBox); + } catch (final RendererException | AssetNotFoundException | UnsupportedOperationException e) { + EditorUtil.handleException(LOGGER, this, e); + testBox.setMaterial(EDITOR.getDefaultMaterial()); + testQuad.setMaterial(EDITOR.getDefaultMaterial()); + testSphere.setMaterial(EDITOR.getDefaultMaterial()); + } + } + + /** + * Change the {@link ModelType}. + * + * @param modelType the model type + */ + public void changeMode(@NotNull final ModelType modelType) { + EXECUTOR_MANAGER.addJMETask(() -> changeModeImpl(modelType)); + } + + /** + * Change the {@link ModelType} in the {@link EditorThread}. + */ + protected void changeModeImpl(@NotNull final ModelType modelType) { + + final Node modelNode = getModelNode(); + modelNode.detachAllChildren(); + + switch (modelType) { + case BOX: { + modelNode.attachChild(getTestBox()); + break; + } + case QUAD: { + modelNode.attachChild(getTestQuad()); + break; + } + case SPHERE: { + modelNode.attachChild(getTestSphere()); + break; + } + } + + setCurrentModelType(modelType); + } + + /** + * Change the {@link Bucket}. + * + * @param bucket the bucket + */ + public void changeBucketType(@NotNull final Bucket bucket) { + EXECUTOR_MANAGER.addJMETask(() -> changeBucketTypeImpl(bucket)); + } + + /** + * Change the {@link Bucket} in the {@link EditorThread}. + */ + protected void changeBucketTypeImpl(@NotNull final Bucket bucket) { + + final Geometry testQuad = getTestQuad(); + testQuad.setQueueBucket(bucket); + + final Geometry testSphere = getTestSphere(); + testSphere.setQueueBucket(bucket); + + final Geometry testBox = getTestBox(); + testBox.setQueueBucket(bucket); + } + + @Override + public void initialize(@NotNull final AppStateManager stateManager, @NotNull final Application application) { + super.initialize(stateManager, application); + changeModeImpl(getCurrentModelType()); + } + + @Override + protected boolean needMovableCamera() { + return false; + } + + @Override + protected boolean needEditorCamera() { + return true; + } + + @Override + protected boolean needLightForCamera() { + return true; + } + + /** + * @return the current model mode. + */ + protected @NotNull ModelType getCurrentModelType() { + return notNull(currentModelType); + } + + /** + * @param currentModelType the current model mode. + */ + protected void setCurrentModelType(@NotNull final ModelType currentModelType) { + this.currentModelType = currentModelType; + } + + /** + * @return true if the light is enabled. + */ + protected boolean isLightEnabled() { + return lightEnabled; + } + + /** + * @param lightEnabled true if the light is enabled. + */ + protected void setLightEnabled(final boolean lightEnabled) { + this.lightEnabled = lightEnabled; + } + + /** + * Update the light in the scene. + * + * @param enabled the enabled + */ + public void updateLightEnabled(final boolean enabled) { + EXECUTOR_MANAGER.addJMETask(() -> updateLightEnabledImpl(enabled)); + } + + /** + * Update the light in the scene in the {@link EditorThread}. + */ + protected void updateLightEnabledImpl(boolean enabled) { + if (enabled == isLightEnabled()) return; + + final DirectionalLight light = getLightForCamera(); + final Node stateNode = getStateNode(); + + if (enabled) { + stateNode.addLight(light); + } else { + stateNode.removeLight(light); + } + + setLightEnabled(enabled); + } + + @Override + protected boolean needUpdateCameraLight() { + return false; + } + + /** + * The enum Model type. + */ + public enum ModelType { + /** + * Sphere model type. + */ + SPHERE, + /** + * Box model type. + */ + BOX, + /** + * Quad model type. + */ + QUAD; + + private static final ModelType[] VALUES = values(); + + /** + * Value of model type. + * + * @param index the index + * @return the model type + */ + public static ModelType valueOf(final int index) { + return VALUES[index]; + } + } +} diff --git a/src/main/java/com/ss/editor/plugin/api/editor/material/BaseMaterialFileEditor.java b/src/main/java/com/ss/editor/plugin/api/editor/material/BaseMaterialFileEditor.java new file mode 100644 index 00000000..021a2dc5 --- /dev/null +++ b/src/main/java/com/ss/editor/plugin/api/editor/material/BaseMaterialFileEditor.java @@ -0,0 +1,409 @@ +package com.ss.editor.plugin.api.editor.material; + +import static com.jme3.renderer.queue.RenderQueue.Bucket.Inherit; +import static com.jme3.renderer.queue.RenderQueue.Bucket.values; +import static com.ss.rlib.util.ClassUtils.unsafeCast; +import static com.ss.rlib.util.ObjectUtils.notNull; +import static javafx.collections.FXCollections.observableArrayList; +import com.jme3.material.Material; +import com.jme3.renderer.queue.RenderQueue; +import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; +import com.ss.editor.model.undo.editor.ChangeConsumer; +import com.ss.editor.plugin.api.editor.Advanced3DFileEditorWithSplitRightTool; +import com.ss.editor.plugin.api.editor.material.BaseMaterialEditor3DState.ModelType; +import com.ss.editor.ui.Icons; +import com.ss.editor.ui.component.editor.state.EditorState; +import com.ss.editor.ui.component.editor.state.impl.EditorMaterialEditorState; +import com.ss.editor.ui.component.tab.EditorToolComponent; +import com.ss.editor.ui.control.property.PropertyEditor; +import com.ss.editor.ui.control.tree.NodeTree; +import com.ss.editor.ui.control.tree.node.TreeNode; +import com.ss.editor.ui.css.CSSClasses; +import com.ss.editor.ui.util.DynamicIconSupport; +import com.ss.rlib.ui.util.FXUtils; +import javafx.collections.ObservableList; +import javafx.scene.control.*; +import javafx.scene.image.ImageView; +import javafx.scene.input.KeyCode; +import javafx.scene.layout.HBox; +import javafx.scene.layout.StackPane; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.function.Supplier; + +/** + * The implementation of the Editor to edit materials. + * + * @author JavaSaBr + */ +public abstract class BaseMaterialFileEditor extends + Advanced3DFileEditorWithSplitRightTool { + + /** + * The default state of editor light. + */ + public static final boolean DEFAULT_LIGHT_ENABLED = true; + + /** + * The list of available bucket types. + */ + @NotNull + protected static final ObservableList BUCKETS = observableArrayList(values()); + + /** + * The settings tree. + */ + @Nullable + private NodeTree settingsTree; + + /** + * The property editor. + */ + @Nullable + private PropertyEditor propertyEditor; + + /** + * The button to use a cube. + */ + @Nullable + private ToggleButton cubeButton; + + /** + * The button to use a sphere. + */ + @Nullable + private ToggleButton sphereButton; + + /** + * The button to use a plane. + */ + @Nullable + private ToggleButton planeButton; + + /** + * The button to use a light. + */ + @Nullable + private ToggleButton lightButton; + + /** + * The list of RenderQueue.Bucket. + */ + @Nullable + private ComboBox bucketComboBox; + + protected BaseMaterialFileEditor() { + super(); + } + + /** + * Get the change consumer. + * + * @return the change consumer. + */ + protected C getChangeConsumer() { + return unsafeCast(this); + } + + @Override + @FXThread + protected boolean handleKeyActionImpl(@NotNull final KeyCode keyCode, final boolean isPressed, + final boolean isControlDown, final boolean isButtonMiddleDown) { + + if (isPressed && isControlDown && keyCode == KeyCode.Z) { + undo(); + return true; + } else if (isPressed && isControlDown && keyCode == KeyCode.Y) { + redo(); + return true; + } else if (isPressed && keyCode == KeyCode.C && !isControlDown && !isButtonMiddleDown) { + final ToggleButton cubeButton = getCubeButton(); + cubeButton.setSelected(true); + return true; + } else if (isPressed && keyCode == KeyCode.S && !isControlDown && !isButtonMiddleDown) { + final ToggleButton sphereButton = getSphereButton(); + sphereButton.setSelected(true); + return true; + } else if (isPressed && keyCode == KeyCode.P && !isControlDown && !isButtonMiddleDown) { + final ToggleButton planeButton = getPlaneButton(); + planeButton.setSelected(true); + return true; + } else if (isPressed && keyCode == KeyCode.L && !isControlDown && !isButtonMiddleDown) { + final ToggleButton lightButton = getLightButton(); + lightButton.setSelected(!lightButton.isSelected()); + return true; + } + + return super.handleKeyActionImpl(keyCode, isPressed, isControlDown, isButtonMiddleDown); + } + + @Override + @FXThread + protected void createToolComponents(@NotNull final EditorToolComponent container, @NotNull final StackPane root) { + super.createToolComponents(container, root); + + settingsTree = new NodeTree<>(this::selectedFromTree, getChangeConsumer()); + propertyEditor = new PropertyEditor<>(getChangeConsumer()); + propertyEditor.prefHeightProperty().bind(root.heightProperty()); + + container.addComponent(buildSplitComponent(settingsTree, propertyEditor, root), getSettingsTreeToolName()); + + FXUtils.addClassTo(settingsTree.getTreeView(), CSSClasses.TRANSPARENT_TREE_VIEW); + } + + /** + * Get the settings tree tool name. + * + * @return the settings tree tool name. + */ + protected @NotNull String getSettingsTreeToolName() { + return Messages.MATERIAL_SETTINGS_MAIN; + } + + /** + * @return the settings tree. + */ + @FromAnyThread + protected @NotNull NodeTree getSettingsTree() { + return notNull(settingsTree); + } + + /** + * @return the property editor. + */ + @FromAnyThread + protected @NotNull PropertyEditor getPropertyEditor() { + return notNull(propertyEditor); + } + + /** + * Handle selected object from tree. + * + * @param object the selected object. + */ + private void selectedFromTree(@Nullable final Object object) { + + Object parent = null; + Object element; + + if (object instanceof TreeNode) { + final TreeNode treeNode = (TreeNode) object; + final TreeNode parentNode = treeNode.getParent(); + parent = parentNode == null ? null : parentNode.getElement(); + element = treeNode.getElement(); + } else { + element = object; + } + + getPropertyEditor().buildFor(element, parent); + } + + @Override + @FXThread + protected void loadState() { + super.loadState(); + + switch (ModelType.valueOf(editorState.getModelType())) { + case BOX: + getCubeButton().setSelected(true); + break; + case SPHERE: + getSphereButton().setSelected(true); + break; + case QUAD: + getPlaneButton().setSelected(true); + break; + } + + getBucketComboBox().getSelectionModel().select(editorState.getBucketType()); + getLightButton().setSelected(editorState.isLightEnable()); + } + + @Override + protected @Nullable Supplier getEditorStateFactory() { + return EditorMaterialEditorState::new; + } + + @Override + @FXThread + protected void calcVSplitSize(@NotNull final SplitPane splitPane) { + splitPane.setDividerPosition(0, 0.2); + } + + @Override + @FXThread + protected boolean needToolbar() { + return true; + } + + @Override + @FXThread + protected void createToolbar(@NotNull final HBox container) { + + cubeButton = new ToggleButton(); + cubeButton.setTooltip(new Tooltip(Messages.MATERIAL_FILE_EDITOR_ACTION_CUBE + " (C)")); + cubeButton.setGraphic(new ImageView(Icons.CUBE_16)); + cubeButton.selectedProperty().addListener((observable, oldValue, newValue) -> + changeModelType(ModelType.BOX, newValue)); + + sphereButton = new ToggleButton(); + sphereButton.setTooltip(new Tooltip(Messages.MATERIAL_FILE_EDITOR_ACTION_SPHERE + " (S)")); + sphereButton.setGraphic(new ImageView(Icons.SPHERE_16)); + sphereButton.selectedProperty().addListener((observable, oldValue, newValue) -> + changeModelType(ModelType.SPHERE, newValue)); + + planeButton = new ToggleButton(); + planeButton.setTooltip(new Tooltip(Messages.MATERIAL_FILE_EDITOR_ACTION_PLANE + " (P)")); + planeButton.setGraphic(new ImageView(Icons.PLANE_16)); + planeButton.selectedProperty().addListener((observable, oldValue, newValue) -> + changeModelType(ModelType.QUAD, newValue)); + + lightButton = new ToggleButton(); + lightButton.setTooltip(new Tooltip(Messages.MATERIAL_FILE_EDITOR_ACTION_LIGHT + " (L)")); + lightButton.setGraphic(new ImageView(Icons.LIGHT_16)); + lightButton.setSelected(DEFAULT_LIGHT_ENABLED); + lightButton.selectedProperty().addListener((observable, oldValue, newValue) -> changeLight(newValue)); + + final Label bucketLabel = new Label(Messages.MATERIAL_FILE_EDITOR_BUCKET_TYPE_LABEL + ":"); + + bucketComboBox = new ComboBox<>(BUCKETS); + bucketComboBox.getSelectionModel().select(Inherit); + bucketComboBox.getSelectionModel() + .selectedItemProperty() + .addListener((observable, oldValue, newValue) -> changeBucketType(newValue)); + + FXUtils.addToPane(createSaveAction(), container); + FXUtils.addToPane(cubeButton, container); + FXUtils.addToPane(sphereButton, container); + FXUtils.addToPane(planeButton, container); + FXUtils.addToPane(lightButton, container); + FXUtils.addToPane(bucketLabel, container); + FXUtils.addToPane(bucketComboBox, container); + + DynamicIconSupport.addSupport(cubeButton, sphereButton, planeButton, lightButton); + + FXUtils.addClassTo(cubeButton, sphereButton, planeButton, lightButton, CSSClasses.FILE_EDITOR_TOOLBAR_BUTTON); + } + + /** + * Handle changing the bucket type. + */ + @FXThread + private void changeBucketType(@NotNull final RenderQueue.Bucket newValue) { + + final T editor3DState = getEditor3DState(); + editor3DState.changeBucketType(newValue); + + final EditorMaterialEditorState editorState = getEditorState(); + if (editorState != null) editorState.setBucketType(newValue); + } + + /** + * Handle changing the light enabling. + */ + @FXThread + private void changeLight(@NotNull final Boolean newValue) { + + final T editor3DState = getEditor3DState(); + editor3DState.updateLightEnabled(newValue); + + final EditorMaterialEditorState editorState = getEditorState(); + if (editorState != null) editorState.setLightEnable(newValue); + } + + /** + * @return the button to use a cube. + */ + @FromAnyThread + private @NotNull ToggleButton getCubeButton() { + return notNull(cubeButton); + } + + /** + * @return the button to use a plane. + */ + @FromAnyThread + private @NotNull ToggleButton getPlaneButton() { + return notNull(planeButton); + } + + /** + * @return the button to use a sphere. + */ + @FromAnyThread + private @NotNull ToggleButton getSphereButton() { + return notNull(sphereButton); + } + + /** + * @return the button to use a light. + */ + @FromAnyThread + private @NotNull ToggleButton getLightButton() { + return notNull(lightButton); + } + + /** + * @return the list of RenderQueue.Bucket. + */ + @FromAnyThread + private @NotNull ComboBox getBucketComboBox() { + return notNull(bucketComboBox); + } + + /** + * Handle the changed model type. + */ + @FXThread + private void changeModelType(@NotNull final ModelType modelType, @NotNull final Boolean newValue) { + if (newValue == Boolean.FALSE) return; + + final T editor3DState = getEditor3DState(); + + final ToggleButton cubeButton = getCubeButton(); + final ToggleButton sphereButton = getSphereButton(); + final ToggleButton planeButton = getPlaneButton(); + + if (modelType == ModelType.BOX) { + cubeButton.setMouseTransparent(true); + sphereButton.setMouseTransparent(false); + planeButton.setMouseTransparent(false); + cubeButton.setSelected(true); + sphereButton.setSelected(false); + planeButton.setSelected(false); + editor3DState.changeMode(modelType); + } else if (modelType == ModelType.SPHERE) { + cubeButton.setMouseTransparent(false); + sphereButton.setMouseTransparent(true); + planeButton.setMouseTransparent(false); + cubeButton.setSelected(false); + sphereButton.setSelected(true); + planeButton.setSelected(false); + editor3DState.changeMode(modelType); + } else if (modelType == ModelType.QUAD) { + cubeButton.setMouseTransparent(false); + sphereButton.setMouseTransparent(false); + planeButton.setMouseTransparent(true); + sphereButton.setSelected(false); + cubeButton.setSelected(false); + planeButton.setSelected(true); + editor3DState.changeMode(modelType); + } + + final EditorMaterialEditorState editorState = getEditorState(); + if (editorState != null) editorState.setModelType(modelType); + } + + @Override + @FXThread + public void notifyFXChangeProperty(@NotNull final Object object, @NotNull final String propertyName) { + if (object instanceof Material) { + getPropertyEditor().refresh(); + } else { + getPropertyEditor().syncFor(object); + } + } +} diff --git a/src/main/java/com/ss/editor/state/editor/impl/material/MaterialEditor3DState.java b/src/main/java/com/ss/editor/state/editor/impl/material/MaterialEditor3DState.java index d3308ae0..a2b01caf 100644 --- a/src/main/java/com/ss/editor/state/editor/impl/material/MaterialEditor3DState.java +++ b/src/main/java/com/ss/editor/state/editor/impl/material/MaterialEditor3DState.java @@ -1,368 +1,17 @@ package com.ss.editor.state.editor.impl.material; -import static com.ss.rlib.util.ObjectUtils.notNull; -import com.jme3.app.Application; -import com.jme3.app.state.AppStateManager; -import com.jme3.asset.AssetNotFoundException; -import com.jme3.input.InputManager; -import com.jme3.input.KeyInput; -import com.jme3.input.controls.KeyTrigger; -import com.jme3.light.DirectionalLight; -import com.jme3.material.Material; -import com.jme3.math.Vector3f; -import com.jme3.renderer.RenderManager; -import com.jme3.renderer.RendererException; -import com.jme3.renderer.queue.RenderQueue.Bucket; -import com.jme3.scene.Geometry; -import com.jme3.scene.Node; -import com.jme3.scene.shape.Box; -import com.jme3.scene.shape.Quad; -import com.jme3.scene.shape.Sphere; -import com.ss.editor.EditorThread; -import com.ss.editor.model.EditorCamera; -import com.ss.editor.model.tool.TangentGenerator; -import com.ss.editor.plugin.api.editor.Advanced3DFileEditor; -import com.ss.editor.plugin.api.editor.part3d.AdvancedPBRWithStudioSky3DEditorState; +import com.ss.editor.plugin.api.editor.material.BaseMaterialEditor3DState; import com.ss.editor.ui.component.editor.impl.material.MaterialFileEditor; -import com.ss.editor.util.EditorUtil; -import com.ss.rlib.function.BooleanFloatConsumer; -import com.ss.rlib.geom.util.AngleUtils; -import com.ss.rlib.util.dictionary.ObjectDictionary; -import javafx.scene.input.KeyCode; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; /** * The implementation the 3D part of the {@link MaterialFileEditor} but it can be reused in other cases. * * @author JavaSaBr */ -public class MaterialEditor3DState extends AdvancedPBRWithStudioSky3DEditorState { +public class MaterialEditor3DState extends BaseMaterialEditor3DState { - @NotNull - private static final Vector3f QUAD_OFFSET = new Vector3f(0, -2, 2); - - @NotNull - private static final Vector3f LIGHT_DIRECTION = new Vector3f(0.007654993F, 0.39636374F, 0.9180617F).negate(); - - private static final float H_ROTATION = AngleUtils.degreeToRadians(75); - private static final float V_ROTATION = AngleUtils.degreeToRadians(25); - - @NotNull - private static final String KEY_C = "SSEditor.materialEditorState.C"; - - @NotNull - private static final String KEY_S = "SSEditor.materialEditorState.S"; - - @NotNull - private static final String KEY_P = "SSEditor.materialEditorState.P"; - - @NotNull - private static final String KEY_L = "SSEditor.materialEditorState.L"; - - static { - TRIGGERS.put(KEY_C, new KeyTrigger(KeyInput.KEY_C)); - TRIGGERS.put(KEY_S, new KeyTrigger(KeyInput.KEY_S)); - TRIGGERS.put(KEY_P, new KeyTrigger(KeyInput.KEY_P)); - TRIGGERS.put(KEY_L, new KeyTrigger(KeyInput.KEY_L)); - } - - /** - * The test box. - */ - @NotNull - private final Geometry testBox; - - /** - * The test sphere. - */ - @NotNull - private final Geometry testSphere; - - /** - * The test quad. - */ - @NotNull - private final Geometry testQuad; - - /** - * The current model mode. - */ - @Nullable - private ModelType currentModelType; - - /** - * The flag of enabling light. - */ - private boolean lightEnabled; - - /** - * Instantiates a new Material editor app state. - * - * @param fileEditor the file editor - */ - public MaterialEditor3DState(@NotNull final T fileEditor) { + public MaterialEditor3DState(@NotNull final MaterialFileEditor fileEditor) { super(fileEditor); - this.testBox = new Geometry("Box", new Box(2, 2, 2)); - this.testSphere = new Geometry("Sphere", new Sphere(30, 30, 2)); - this.testQuad = new Geometry("Quad", new Quad(4, 4)); - this.testQuad.setLocalTranslation(QUAD_OFFSET); - this.lightEnabled = MaterialFileEditor.DEFAULT_LIGHT_ENABLED; - - TangentGenerator.useMikktspaceGenerator(testBox); - TangentGenerator.useMikktspaceGenerator(testSphere); - TangentGenerator.useMikktspaceGenerator(testQuad); - - final DirectionalLight light = notNull(getLightForCamera()); - light.setDirection(LIGHT_DIRECTION); - - final EditorCamera editorCamera = notNull(getEditorCamera()); - editorCamera.setDefaultHorizontalRotation(H_ROTATION); - editorCamera.setDefaultVerticalRotation(V_ROTATION); - - getModelNode().attachChild(getNodeForCamera()); - } - - @Override - protected void registerActionHandlers(@NotNull final ObjectDictionary actionHandlers) { - super.registerActionHandlers(actionHandlers); - - final T fileEditor = getFileEditor(); - - actionHandlers.put(KEY_S, (isPressed, tpf) -> fileEditor.handleKeyAction(KeyCode.S, isPressed, isControlDown(), isButtonMiddleDown())); - actionHandlers.put(KEY_C, (isPressed, tpf) -> fileEditor.handleKeyAction(KeyCode.C, isPressed, isControlDown(), isButtonMiddleDown())); - actionHandlers.put(KEY_P, (isPressed, tpf) -> fileEditor.handleKeyAction(KeyCode.P, isPressed, isControlDown(), isButtonMiddleDown())); - actionHandlers.put(KEY_L, (isPressed, tpf) -> fileEditor.handleKeyAction(KeyCode.L, isPressed, isControlDown(), isButtonMiddleDown())); - } - - @Override - protected void registerActionListener(@NotNull final InputManager inputManager) { - super.registerActionListener(inputManager); - inputManager.addListener(actionListener, KEY_S, KEY_C, KEY_P, KEY_L); - } - - /** - * @return the test box. - */ - protected @NotNull Geometry getTestBox() { - return testBox; - } - - /** - * @return the test quad. - */ - protected @NotNull Geometry getTestQuad() { - return testQuad; - } - - /** - * @return the test sphere. - */ - protected @NotNull Geometry getTestSphere() { - return testSphere; - } - - /** - * Update the {@link Material}. - * - * @param material the material - */ - public void updateMaterial(@NotNull final Material material) { - EXECUTOR_MANAGER.addJMETask(() -> updateMaterialImpl(material)); - } - - /** - * Update the {@link Material} in the {@link EditorThread}. - */ - protected void updateMaterialImpl(@NotNull final Material material) { - - final Geometry testBox = getTestBox(); - testBox.setMaterial(material); - - final Geometry testQuad = getTestQuad(); - testQuad.setMaterial(material); - - final Geometry testSphere = getTestSphere(); - testSphere.setMaterial(material); - - final RenderManager renderManager = EDITOR.getRenderManager(); - try { - renderManager.preloadScene(testBox); - } catch (final RendererException | AssetNotFoundException | UnsupportedOperationException e) { - EditorUtil.handleException(LOGGER, this, e); - testBox.setMaterial(EDITOR.getDefaultMaterial()); - testQuad.setMaterial(EDITOR.getDefaultMaterial()); - testSphere.setMaterial(EDITOR.getDefaultMaterial()); - } - } - - /** - * Change the {@link ModelType}. - * - * @param modelType the model type - */ - public void changeMode(@NotNull final ModelType modelType) { - EXECUTOR_MANAGER.addJMETask(() -> changeModeImpl(modelType)); - } - - /** - * Change the {@link ModelType} in the {@link EditorThread}. - */ - protected void changeModeImpl(@NotNull final ModelType modelType) { - - final Node modelNode = getModelNode(); - modelNode.detachAllChildren(); - - switch (modelType) { - case BOX: { - modelNode.attachChild(getTestBox()); - break; - } - case QUAD: { - modelNode.attachChild(getTestQuad()); - break; - } - case SPHERE: { - modelNode.attachChild(getTestSphere()); - break; - } - } - - setCurrentModelType(modelType); - } - - /** - * Change the {@link Bucket}. - * - * @param bucket the bucket - */ - public void changeBucketType(@NotNull final Bucket bucket) { - EXECUTOR_MANAGER.addJMETask(() -> changeBucketTypeImpl(bucket)); - } - - /** - * Change the {@link Bucket} in the {@link EditorThread}. - */ - protected void changeBucketTypeImpl(@NotNull final Bucket bucket) { - - final Geometry testQuad = getTestQuad(); - testQuad.setQueueBucket(bucket); - - final Geometry testSphere = getTestSphere(); - testSphere.setQueueBucket(bucket); - - final Geometry testBox = getTestBox(); - testBox.setQueueBucket(bucket); - } - - @Override - public void initialize(@NotNull final AppStateManager stateManager, @NotNull final Application application) { - super.initialize(stateManager, application); - changeModeImpl(getCurrentModelType()); - } - - @Override - protected boolean needMovableCamera() { - return false; - } - - @Override - protected boolean needEditorCamera() { - return true; - } - - @Override - protected boolean needLightForCamera() { - return true; - } - - /** - * @return the current model mode. - */ - protected @NotNull ModelType getCurrentModelType() { - return notNull(currentModelType); - } - - /** - * @param currentModelType the current model mode. - */ - protected void setCurrentModelType(@NotNull final ModelType currentModelType) { - this.currentModelType = currentModelType; - } - - /** - * @return true if the light is enabled. - */ - protected boolean isLightEnabled() { - return lightEnabled; - } - - /** - * @param lightEnabled true if the light is enabled. - */ - protected void setLightEnabled(final boolean lightEnabled) { - this.lightEnabled = lightEnabled; - } - - /** - * Update the light in the scene. - * - * @param enabled the enabled - */ - public void updateLightEnabled(final boolean enabled) { - EXECUTOR_MANAGER.addJMETask(() -> updateLightEnabledImpl(enabled)); - } - - /** - * Update the light in the scene in the {@link EditorThread}. - */ - protected void updateLightEnabledImpl(boolean enabled) { - if (enabled == isLightEnabled()) return; - - final DirectionalLight light = getLightForCamera(); - final Node stateNode = getStateNode(); - - if (enabled) { - stateNode.addLight(light); - } else { - stateNode.removeLight(light); - } - - setLightEnabled(enabled); - } - - @Override - protected boolean needUpdateCameraLight() { - return false; - } - - /** - * The enum Model type. - */ - public enum ModelType { - /** - * Sphere model type. - */ - SPHERE, - /** - * Box model type. - */ - BOX, - /** - * Quad model type. - */ - QUAD; - - private static final ModelType[] VALUES = values(); - - /** - * Value of model type. - * - * @param index the index - * @return the model type - */ - public static ModelType valueOf(final int index) { - return VALUES[index]; - } } } diff --git a/src/main/java/com/ss/editor/ui/component/editor/impl/material/MaterialFileEditor.java b/src/main/java/com/ss/editor/ui/component/editor/impl/material/MaterialFileEditor.java index 92f065c4..a256dd37 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/impl/material/MaterialFileEditor.java +++ b/src/main/java/com/ss/editor/ui/component/editor/impl/material/MaterialFileEditor.java @@ -1,13 +1,10 @@ package com.ss.editor.ui.component.editor.impl.material; -import static com.jme3.renderer.queue.RenderQueue.Bucket.Inherit; -import static com.jme3.renderer.queue.RenderQueue.Bucket.values; import static com.ss.editor.Messages.MATERIAL_EDITOR_NAME; import static com.ss.editor.util.EditorUtil.getAssetFile; import static com.ss.editor.util.EditorUtil.toAssetPath; import static com.ss.editor.util.MaterialUtils.updateMaterialIdNeed; import static com.ss.rlib.util.ObjectUtils.notNull; -import static javafx.collections.FXCollections.observableArrayList; import com.jme3.asset.AssetManager; import com.jme3.asset.MaterialKey; import com.jme3.asset.TextureKey; @@ -15,7 +12,6 @@ import com.jme3.material.MatParamTexture; import com.jme3.material.Material; import com.jme3.material.MaterialDef; -import com.jme3.renderer.queue.RenderQueue; import com.jme3.shader.VarType; import com.jme3.texture.Texture; import com.ss.editor.FileExtensions; @@ -28,33 +24,24 @@ import com.ss.editor.model.node.material.RootMaterialSettings; import com.ss.editor.model.undo.EditorOperationControl; import com.ss.editor.model.undo.editor.ChangeConsumer; -import com.ss.editor.model.undo.editor.MaterialChangeConsumer; -import com.ss.editor.plugin.api.editor.Advanced3DFileEditorWithSplitRightTool; +import com.ss.editor.plugin.api.editor.material.BaseMaterialEditor3DState.ModelType; +import com.ss.editor.plugin.api.editor.material.BaseMaterialFileEditor; import com.ss.editor.serializer.MaterialSerializer; import com.ss.editor.state.editor.impl.material.MaterialEditor3DState; -import com.ss.editor.state.editor.impl.material.MaterialEditor3DState.ModelType; -import com.ss.editor.ui.Icons; import com.ss.editor.ui.component.editor.EditorDescription; import com.ss.editor.ui.component.editor.state.EditorState; import com.ss.editor.ui.component.editor.state.impl.EditorMaterialEditorState; -import com.ss.editor.ui.component.tab.EditorToolComponent; -import com.ss.editor.ui.control.property.PropertyEditor; import com.ss.editor.ui.control.property.operation.PropertyOperation; -import com.ss.editor.ui.control.tree.NodeTree; -import com.ss.editor.ui.control.tree.node.TreeNode; import com.ss.editor.ui.css.CSSClasses; import com.ss.editor.ui.event.impl.FileChangedEvent; -import com.ss.editor.ui.util.DynamicIconSupport; import com.ss.editor.ui.util.UIUtils; import com.ss.editor.util.MaterialUtils; import com.ss.rlib.ui.util.FXUtils; import javafx.collections.ObservableList; -import javafx.scene.control.*; -import javafx.scene.image.ImageView; +import javafx.scene.control.ComboBox; +import javafx.scene.control.Label; import javafx.scene.input.DragEvent; -import javafx.scene.input.KeyCode; import javafx.scene.layout.HBox; -import javafx.scene.layout.StackPane; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -73,7 +60,7 @@ * @author JavaSaBr */ public class MaterialFileEditor extends - Advanced3DFileEditorWithSplitRightTool implements MaterialChangeConsumer { + BaseMaterialFileEditor { /** * The constant DESCRIPTION. @@ -88,28 +75,14 @@ public class MaterialFileEditor extends DESCRIPTION.addExtension(FileExtensions.JME_MATERIAL); } - /** - * The default flag of enabling light. - */ - public static final boolean DEFAULT_LIGHT_ENABLED = true; - @NotNull private static final ResourceManager RESOURCE_MANAGER = ResourceManager.getInstance(); - @NotNull - private static final ObservableList BUCKETS = observableArrayList(values()); - - /** - * The settings tree. - */ - @Nullable - private NodeTree settingsTree; - /** - * The property editor. + * The list of material definitions. */ @Nullable - private PropertyEditor propertyEditor; + private ComboBox materialDefinitionBox; /** * The current editing material. @@ -117,42 +90,6 @@ public class MaterialFileEditor extends @Nullable private Material currentMaterial; - /** - * The button to use a cube. - */ - @Nullable - private ToggleButton cubeButton; - - /** - * The button to use a sphere. - */ - @Nullable - private ToggleButton sphereButton; - - /** - * The button to use a plane. - */ - @Nullable - private ToggleButton planeButton; - - /** - * The button to use a light. - */ - @Nullable - private ToggleButton lightButton; - - /** - * The list of RenderQueue.Bucket. - */ - @Nullable - private ComboBox bucketComboBox; - - /** - * The list of material definitions. - */ - @Nullable - private ComboBox materialDefinitionBox; - private MaterialFileEditor() { super(); } @@ -160,7 +97,7 @@ private MaterialFileEditor() { @Override @FXThread protected @NotNull MaterialEditor3DState create3DEditorState() { - return new MaterialEditor3DState<>(this); + return new MaterialEditor3DState(this); } @Override @@ -209,90 +146,6 @@ protected void handleExternalChanges() { operationControl.clear(); } - @Override - @FXThread - protected boolean handleKeyActionImpl(@NotNull final KeyCode keyCode, final boolean isPressed, - final boolean isControlDown, final boolean isButtonMiddleDown) { - - if (isPressed && isControlDown && keyCode == KeyCode.Z) { - undo(); - return true; - } else if (isPressed && isControlDown && keyCode == KeyCode.Y) { - redo(); - return true; - } else if (isPressed && keyCode == KeyCode.C && !isControlDown && !isButtonMiddleDown) { - final ToggleButton cubeButton = getCubeButton(); - cubeButton.setSelected(true); - return true; - } else if (isPressed && keyCode == KeyCode.S && !isControlDown && !isButtonMiddleDown) { - final ToggleButton sphereButton = getSphereButton(); - sphereButton.setSelected(true); - return true; - } else if (isPressed && keyCode == KeyCode.P && !isControlDown && !isButtonMiddleDown) { - final ToggleButton planeButton = getPlaneButton(); - planeButton.setSelected(true); - return true; - } else if (isPressed && keyCode == KeyCode.L && !isControlDown && !isButtonMiddleDown) { - final ToggleButton lightButton = getLightButton(); - lightButton.setSelected(!lightButton.isSelected()); - return true; - } - - return super.handleKeyActionImpl(keyCode, isPressed, isControlDown, isButtonMiddleDown); - } - - @Override - @FXThread - protected void createToolComponents(@NotNull final EditorToolComponent container, @NotNull final StackPane root) { - super.createToolComponents(container, root); - - settingsTree = new NodeTree<>(this::selectedFromTree, this); - propertyEditor = new PropertyEditor<>(this); - propertyEditor.prefHeightProperty().bind(root.heightProperty()); - - container.addComponent(buildSplitComponent(settingsTree, propertyEditor, root), Messages.MATERIAL_SETTINGS_MAIN); - - FXUtils.addClassTo(settingsTree.getTreeView(), CSSClasses.TRANSPARENT_TREE_VIEW); - } - - /** - * @return the settings tree. - */ - @FromAnyThread - private @NotNull NodeTree getSettingsTree() { - return notNull(settingsTree); - } - - /** - * @return the property editor. - */ - @FromAnyThread - private @NotNull PropertyEditor getPropertyEditor() { - return notNull(propertyEditor); - } - - /** - * Handle selected object from tree. - * - * @param object the selected object. - */ - private void selectedFromTree(@Nullable final Object object) { - - Object parent = null; - Object element; - - if (object instanceof TreeNode) { - final TreeNode treeNode = (TreeNode) object; - final TreeNode parentNode = treeNode.getParent(); - parent = parentNode == null ? null : parentNode.getElement(); - element = treeNode.getElement(); - } else { - element = object; - } - - getPropertyEditor().buildFor(element, parent); - } - /** * Try to apply dropped texture. * @@ -380,38 +233,11 @@ protected void doOpenFile(@NotNull final Path file) { reload(material); } - @Override - @FXThread - protected void loadState() { - super.loadState(); - - switch (ModelType.valueOf(editorState.getModelType())) { - case BOX: - getCubeButton().setSelected(true); - break; - case SPHERE: - getSphereButton().setSelected(true); - break; - case QUAD: - getPlaneButton().setSelected(true); - break; - } - - getBucketComboBox().getSelectionModel().select(editorState.getBucketType()); - getLightButton().setSelected(editorState.isLightEnable()); - } - @Override protected @Nullable Supplier getEditorStateFactory() { return EditorMaterialEditorState::new; } - @Override - @FXThread - protected void calcVSplitSize(@NotNull final SplitPane splitPane) { - splitPane.setDividerPosition(0, 0.2); - } - /** * Reload the material. */ @@ -447,39 +273,10 @@ private void reload(@NotNull final Material material) { return notNull(materialDefinitionBox); } - @Override - @FXThread - protected boolean needToolbar() { - return true; - } - @Override @FXThread protected void createToolbar(@NotNull final HBox container) { - - cubeButton = new ToggleButton(); - cubeButton.setTooltip(new Tooltip(Messages.MATERIAL_FILE_EDITOR_ACTION_CUBE + " (C)")); - cubeButton.setGraphic(new ImageView(Icons.CUBE_16)); - cubeButton.selectedProperty().addListener((observable, oldValue, newValue) -> - changeModelType(ModelType.BOX, newValue)); - - sphereButton = new ToggleButton(); - sphereButton.setTooltip(new Tooltip(Messages.MATERIAL_FILE_EDITOR_ACTION_SPHERE + " (S)")); - sphereButton.setGraphic(new ImageView(Icons.SPHERE_16)); - sphereButton.selectedProperty().addListener((observable, oldValue, newValue) -> - changeModelType(ModelType.SPHERE, newValue)); - - planeButton = new ToggleButton(); - planeButton.setTooltip(new Tooltip(Messages.MATERIAL_FILE_EDITOR_ACTION_PLANE + " (P)")); - planeButton.setGraphic(new ImageView(Icons.PLANE_16)); - planeButton.selectedProperty().addListener((observable, oldValue, newValue) -> - changeModelType(ModelType.QUAD, newValue)); - - lightButton = new ToggleButton(); - lightButton.setTooltip(new Tooltip(Messages.MATERIAL_FILE_EDITOR_ACTION_LIGHT + " (L)")); - lightButton.setGraphic(new ImageView(Icons.LIGHT_16)); - lightButton.setSelected(DEFAULT_LIGHT_ENABLED); - lightButton.selectedProperty().addListener((observable, oldValue, newValue) -> changeLight(newValue)); + super.createToolbar(container); final Label materialDefinitionLabel = new Label(Messages.MATERIAL_EDITOR_MATERIAL_TYPE_LABEL + ":"); @@ -488,42 +285,11 @@ protected void createToolbar(@NotNull final HBox container) { .selectedItemProperty() .addListener((observable, oldValue, newValue) -> changeType(newValue)); - final Label bucketLabel = new Label(Messages.MATERIAL_FILE_EDITOR_BUCKET_TYPE_LABEL + ":"); - - bucketComboBox = new ComboBox<>(BUCKETS); - bucketComboBox.getSelectionModel().select(Inherit); - bucketComboBox.getSelectionModel() - .selectedItemProperty() - .addListener((observable, oldValue, newValue) -> changeBucketType(newValue)); - - FXUtils.addToPane(createSaveAction(), container); - FXUtils.addToPane(cubeButton, container); - FXUtils.addToPane(sphereButton, container); - FXUtils.addToPane(planeButton, container); - FXUtils.addToPane(lightButton, container); FXUtils.addToPane(materialDefinitionLabel, container); FXUtils.addToPane(materialDefinitionBox, container); - FXUtils.addToPane(bucketLabel, container); - FXUtils.addToPane(bucketComboBox, container); - - DynamicIconSupport.addSupport(cubeButton, sphereButton, planeButton, lightButton); - - FXUtils.addClassTo(materialDefinitionLabel, bucketLabel, CSSClasses.FILE_EDITOR_TOOLBAR_LABEL); - FXUtils.addClassTo(materialDefinitionBox, bucketComboBox, CSSClasses.FILE_EDITOR_TOOLBAR_FIELD); - FXUtils.addClassTo(cubeButton, sphereButton, planeButton, lightButton, CSSClasses.FILE_EDITOR_TOOLBAR_BUTTON); - } - - /** - * Handle changing the bucket type. - */ - @FXThread - private void changeBucketType(@NotNull final RenderQueue.Bucket newValue) { - final MaterialEditor3DState editor3DState = getEditor3DState(); - editor3DState.changeBucketType(newValue); - - final EditorMaterialEditorState editorState = getEditorState(); - if (editorState != null) editorState.setBucketType(newValue); + FXUtils.addClassTo(materialDefinitionLabel, CSSClasses.FILE_EDITOR_TOOLBAR_LABEL); + FXUtils.addClassTo(materialDefinitionBox, CSSClasses.FILE_EDITOR_TOOLBAR_FIELD); } /** @@ -554,118 +320,11 @@ private void processChangeTypeImpl(@Nullable final String newType) { reload(newMaterial); } - /** - * Handle changing the light enabling. - */ - @FXThread - private void changeLight(@NotNull final Boolean newValue) { - - final MaterialEditor3DState editor3DState = getEditor3DState(); - editor3DState.updateLightEnabled(newValue); - - final EditorMaterialEditorState editorState = getEditorState(); - if (editorState != null) editorState.setLightEnable(newValue); - } - - /** - * @return the button to use a cube. - */ @FromAnyThread - private @NotNull ToggleButton getCubeButton() { - return notNull(cubeButton); - } - - /** - * @return the button to use a plane. - */ - @FromAnyThread - private @NotNull ToggleButton getPlaneButton() { - return notNull(planeButton); - } - - /** - * @return the button to use a sphere. - */ - @FromAnyThread - private @NotNull ToggleButton getSphereButton() { - return notNull(sphereButton); - } - - /** - * @return the button to use a light. - */ - @FromAnyThread - private @NotNull ToggleButton getLightButton() { - return notNull(lightButton); - } - - /** - * @return the list of RenderQueue.Bucket. - */ - @FromAnyThread - private @NotNull ComboBox getBucketComboBox() { - return notNull(bucketComboBox); - } - - /** - * Handle the changed model type. - */ - @FXThread - private void changeModelType(@NotNull final ModelType modelType, @NotNull final Boolean newValue) { - if (newValue == Boolean.FALSE) return; - - final MaterialEditor3DState editor3DState = getEditor3DState(); - - final ToggleButton cubeButton = getCubeButton(); - final ToggleButton sphereButton = getSphereButton(); - final ToggleButton planeButton = getPlaneButton(); - - if (modelType == ModelType.BOX) { - cubeButton.setMouseTransparent(true); - sphereButton.setMouseTransparent(false); - planeButton.setMouseTransparent(false); - cubeButton.setSelected(true); - sphereButton.setSelected(false); - planeButton.setSelected(false); - editor3DState.changeMode(modelType); - } else if (modelType == ModelType.SPHERE) { - cubeButton.setMouseTransparent(false); - sphereButton.setMouseTransparent(true); - planeButton.setMouseTransparent(false); - cubeButton.setSelected(false); - sphereButton.setSelected(true); - planeButton.setSelected(false); - editor3DState.changeMode(modelType); - } else if (modelType == ModelType.QUAD) { - cubeButton.setMouseTransparent(false); - sphereButton.setMouseTransparent(false); - planeButton.setMouseTransparent(true); - sphereButton.setSelected(false); - cubeButton.setSelected(false); - planeButton.setSelected(true); - editor3DState.changeMode(modelType); - } - - final EditorMaterialEditorState editorState = getEditorState(); - if (editorState != null) editorState.setModelType(modelType); - } - - @Override - @FromAnyThread - public @NotNull Material getCurrentMaterial() { + private @NotNull Material getCurrentMaterial() { return notNull(currentMaterial); } - @Override - @FXThread - public void notifyFXChangeProperty(@NotNull final Object object, @NotNull final String propertyName) { - if (object instanceof Material) { - getPropertyEditor().refresh(); - } else { - getPropertyEditor().syncFor(object); - } - } - /** * @param currentMaterial the current editing material. */ diff --git a/src/main/java/com/ss/editor/ui/component/editor/impl/material/operation/RenderStateOperation.java b/src/main/java/com/ss/editor/ui/component/editor/impl/material/operation/RenderStateOperation.java deleted file mode 100644 index e454bdcd..00000000 --- a/src/main/java/com/ss/editor/ui/component/editor/impl/material/operation/RenderStateOperation.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.ss.editor.ui.component.editor.impl.material.operation; - -import com.jme3.material.Material; -import com.jme3.material.RenderState; -import com.ss.editor.manager.ExecutorManager; -import com.ss.editor.model.undo.editor.MaterialChangeConsumer; -import com.ss.editor.model.undo.impl.AbstractEditorOperation; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.function.BiConsumer; - -/** - * The implementation of an editor operation to edit material render params. - * - * @param the type parameter - * @author JavaSaBr - */ -@SuppressWarnings("Duplicates") -public class RenderStateOperation extends AbstractEditorOperation { - - @NotNull - private static final ExecutorManager EXECUTOR_MANAGER = ExecutorManager.getInstance(); - - /** - * The apply handler. - */ - @NotNull - private final BiConsumer applyHandler; - - /** - * The new value. - */ - @Nullable - private final T newValue; - - /** - * The old value. - */ - @Nullable - private final T oldValue; - - /** - * Instantiates a new Render state operation. - * - * @param newValue the new value - * @param oldValue the old value - * @param applyHandler the apply handler - */ - public RenderStateOperation(@Nullable final T newValue, @Nullable final T oldValue, - @NotNull final BiConsumer applyHandler) { - this.newValue = newValue; - this.oldValue = oldValue; - this.applyHandler = applyHandler; - } - - @Override - protected void redoImpl(@NotNull final MaterialChangeConsumer editor) { - EXECUTOR_MANAGER.addJMETask(() -> { - final Material currentMaterial = editor.getCurrentMaterial(); - final RenderState renderState = currentMaterial.getAdditionalRenderState(); - applyHandler.accept(renderState, newValue); - EXECUTOR_MANAGER.addFXTask(() -> editor.notifyFXChangeProperty(renderState, "RenderState")); - }); - } - - @Override - protected void undoImpl(@NotNull final MaterialChangeConsumer editor) { - EXECUTOR_MANAGER.addJMETask(() -> { - final Material currentMaterial = editor.getCurrentMaterial(); - final RenderState renderState = currentMaterial.getAdditionalRenderState(); - applyHandler.accept(renderState, oldValue); - EXECUTOR_MANAGER.addFXTask(() -> editor.notifyFXChangeProperty(renderState, "RenderState")); - }); - } -} diff --git a/src/main/java/com/ss/editor/ui/component/editor/state/impl/EditorMaterialEditorState.java b/src/main/java/com/ss/editor/ui/component/editor/state/impl/EditorMaterialEditorState.java index 9fe63ae7..72f1c2dd 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/state/impl/EditorMaterialEditorState.java +++ b/src/main/java/com/ss/editor/ui/component/editor/state/impl/EditorMaterialEditorState.java @@ -1,7 +1,7 @@ package com.ss.editor.ui.component.editor.state.impl; import com.jme3.renderer.queue.RenderQueue; -import com.ss.editor.state.editor.impl.material.MaterialEditor3DState; +import com.ss.editor.plugin.api.editor.material.BaseMaterialEditor3DState; import com.ss.editor.ui.component.editor.impl.material.MaterialFileEditor; import org.jetbrains.annotations.NotNull; @@ -40,7 +40,7 @@ public class EditorMaterialEditorState extends Editor3DWithEditorToolEditorState * Instantiates a new Material file editor state. */ public EditorMaterialEditorState() { - modelType = MaterialEditor3DState.ModelType.BOX.ordinal(); + modelType = BaseMaterialEditor3DState.ModelType.BOX.ordinal(); bucketTypeId = RenderQueue.Bucket.Inherit.ordinal(); lightEnable = EDITOR_CONFIG.isDefaultEditorCameraEnabled(); } @@ -91,7 +91,7 @@ public int getModelType() { * * @param modelType the model type. */ - public void setModelType(@NotNull final MaterialEditor3DState.ModelType modelType) { + public void setModelType(@NotNull final BaseMaterialEditor3DState.ModelType modelType) { final boolean changed = getModelType() != modelType.ordinal(); this.modelType = modelType.ordinal(); final Runnable changeHandler = getChangeHandler(); From f2debf011ce2d7f88422b1364f2ec410e46e20d6 Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Tue, 5 Sep 2017 19:01:43 +0300 Subject: [PATCH 02/50] 1 --- .../ui/component/editor/impl/material/MaterialFileEditor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/ss/editor/ui/component/editor/impl/material/MaterialFileEditor.java b/src/main/java/com/ss/editor/ui/component/editor/impl/material/MaterialFileEditor.java index a256dd37..c89ed875 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/impl/material/MaterialFileEditor.java +++ b/src/main/java/com/ss/editor/ui/component/editor/impl/material/MaterialFileEditor.java @@ -63,7 +63,7 @@ public class MaterialFileEditor extends BaseMaterialFileEditor { /** - * The constant DESCRIPTION. + * The description. */ @NotNull public static final EditorDescription DESCRIPTION = new EditorDescription(); From 221f2ddca3015ab06d58a291558df1e4dc1a1d6e Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Tue, 5 Sep 2017 23:00:26 +0300 Subject: [PATCH 03/50] updated build script. --- build-native.xml | 4 ++-- .../ss/editor/ui/component/editor/impl/GLSLFileEditor.java | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/build-native.xml b/build-native.xml index c402aa5c..d8f908be 100644 --- a/build-native.xml +++ b/build-native.xml @@ -5,8 +5,8 @@ - + diff --git a/src/main/java/com/ss/editor/ui/component/editor/impl/GLSLFileEditor.java b/src/main/java/com/ss/editor/ui/component/editor/impl/GLSLFileEditor.java index 4bc992ee..d4c6e4bf 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/impl/GLSLFileEditor.java +++ b/src/main/java/com/ss/editor/ui/component/editor/impl/GLSLFileEditor.java @@ -123,15 +123,13 @@ private static StyleSpans> computeHighlighting(@NotNull final return spansBuilder.create(); } - @NotNull @Override - protected StyleSpans> getStyleSpans(@NotNull final String text) { + protected @NotNull StyleSpans> getStyleSpans(@NotNull final String text) { return computeHighlighting(text); } - @NotNull @Override - public EditorDescription getDescription() { + public @NotNull EditorDescription getDescription() { return DESCRIPTION; } } From e86859980d8613790a76e66be4b1925db4430d69 Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Wed, 6 Sep 2017 15:41:07 +0300 Subject: [PATCH 04/50] added some debug information. --- .../java/com/ss/editor/FolderAssetLocator.java | 3 ++- src/main/java/com/ss/editor/JFXApplication.java | 15 +++++---------- .../java/com/ss/editor/config/EditorConfig.java | 3 +++ .../creator/impl/AbstractFileCreator.java | 2 +- .../texture/SingleColorTextureFileCreator.java | 12 ++++-------- 5 files changed, 15 insertions(+), 20 deletions(-) diff --git a/src/main/java/com/ss/editor/FolderAssetLocator.java b/src/main/java/com/ss/editor/FolderAssetLocator.java index 0cc888b5..061c1624 100644 --- a/src/main/java/com/ss/editor/FolderAssetLocator.java +++ b/src/main/java/com/ss/editor/FolderAssetLocator.java @@ -22,12 +22,13 @@ public class FolderAssetLocator implements AssetLocator { @Override public void setRootPath(@NotNull final String rootPath) { - System.out.println(rootPath); } @Override public AssetInfo locate(final AssetManager manager, final AssetKey key) { + System.out.println("Locate " + key.getName() + " in the " + key.getFolder()); + final EditorConfig editorConfig = EditorConfig.getInstance(); final Path currentAsset = editorConfig.getCurrentAsset(); if (currentAsset == null) return null; diff --git a/src/main/java/com/ss/editor/JFXApplication.java b/src/main/java/com/ss/editor/JFXApplication.java index f9f76aa6..21489a12 100644 --- a/src/main/java/com/ss/editor/JFXApplication.java +++ b/src/main/java/com/ss/editor/JFXApplication.java @@ -73,9 +73,8 @@ public class JFXApplication extends Application { * * @return the instance */ - @NotNull @FromAnyThread - public static JFXApplication getInstance() { + public static @NotNull JFXApplication getInstance() { return notNull(instance); } @@ -84,9 +83,8 @@ public static JFXApplication getInstance() { * * @return the current stage. */ - @Nullable @FromAnyThread - private static Stage getStage() { + private static @Nullable Stage getStage() { final JFXApplication instance = JFXApplication.instance; return instance == null ? null : instance.stage; } @@ -245,8 +243,7 @@ public void removeWindow(@NotNull final Window window) { * * @return the last opened window. */ - @NotNull - public Window getLastWindow() { + public @NotNull Window getLastWindow() { return notNull(ArrayUtils.getInReadLock(openedWindows, Array::last)); } @@ -412,9 +409,8 @@ private void createSceneProcessor(@NotNull final EditorFXScene scene, @NotNull f * * @return the JavaFX scene. */ - @NotNull @FromAnyThread - public EditorFXScene getScene() { + public @NotNull EditorFXScene getScene() { return notNull(scene, "Scene can't be null."); } @@ -423,9 +419,8 @@ public EditorFXScene getScene() { * * @return the scene processor. */ - @NotNull @FromAnyThread - public FrameTransferSceneProcessor getSceneProcessor() { + public @NotNull FrameTransferSceneProcessor getSceneProcessor() { return notNull(sceneProcessor, "Scene processor can't be null."); } } diff --git a/src/main/java/com/ss/editor/config/EditorConfig.java b/src/main/java/com/ss/editor/config/EditorConfig.java index 699c8fa8..441347ad 100644 --- a/src/main/java/com/ss/editor/config/EditorConfig.java +++ b/src/main/java/com/ss/editor/config/EditorConfig.java @@ -16,6 +16,7 @@ import com.ss.editor.util.OpenGLVersion; import com.ss.rlib.logging.Logger; import com.ss.rlib.logging.LoggerManager; +import com.ss.rlib.util.Utils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -847,6 +848,8 @@ public void setOpenGLVersion(@NotNull final OpenGLVersion openGLVersion) { @FromAnyThread public AppSettings getSettings() { + System.out.println("Classloader is from :" + Utils.getRootFolderFromClass(AppSettings.class)); + final GraphicsEnvironment graphicsEnvironment = GraphicsEnvironment.getLocalGraphicsEnvironment(); final GraphicsDevice device = graphicsEnvironment.getDefaultScreenDevice(); final DisplayMode displayMode = device.getDisplayMode(); diff --git a/src/main/java/com/ss/editor/ui/component/creator/impl/AbstractFileCreator.java b/src/main/java/com/ss/editor/ui/component/creator/impl/AbstractFileCreator.java index 3f4babd4..d5680fe6 100644 --- a/src/main/java/com/ss/editor/ui/component/creator/impl/AbstractFileCreator.java +++ b/src/main/java/com/ss/editor/ui/component/creator/impl/AbstractFileCreator.java @@ -238,7 +238,7 @@ protected void processOk() { notifyFileCreated(fileToCreate, true); - } catch (final IOException e) { + } catch (final Exception e) { Utils.run(tempFile, Files::delete); EditorUtil.handleException(LOGGER, this, e); } diff --git a/src/main/java/com/ss/editor/ui/component/creator/impl/texture/SingleColorTextureFileCreator.java b/src/main/java/com/ss/editor/ui/component/creator/impl/texture/SingleColorTextureFileCreator.java index 8562a8c4..445fdd33 100644 --- a/src/main/java/com/ss/editor/ui/component/creator/impl/texture/SingleColorTextureFileCreator.java +++ b/src/main/java/com/ss/editor/ui/component/creator/impl/texture/SingleColorTextureFileCreator.java @@ -86,8 +86,7 @@ protected void createPreview(@NotNull final BorderPane container) { /** * @return the image view to show preview of texture. */ - @NotNull - private ImageView getImageView() { + private @NotNull ImageView getImageView() { return notNull(imageView); } @@ -97,22 +96,19 @@ protected boolean needPreview() { return true; } - @NotNull @Override - protected String getTitleText() { + protected @NotNull String getTitleText() { return Messages.SINGLE_COLOR_TEXTURE_FILE_CREATOR_TITLE; } - @NotNull @Override - protected String getFileExtension() { + protected @NotNull String getFileExtension() { return FileExtensions.IMAGE_PNG; } - @NotNull @Override @FromAnyThread - protected Array getPropertyDefinitions() { + protected @NotNull Array getPropertyDefinitions() { return DEFINITIONS; } From c5f61dd199c35c81eeca4fa622df8b510bff2c9b Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Thu, 7 Sep 2017 17:41:51 +0300 Subject: [PATCH 05/50] updated API. --- src/main/java/com/ss/editor/config/EditorConfig.java | 3 --- .../ss/editor/plugin/api/editor/BaseFileEditor.java | 11 +++++++++-- .../editor/impl/material/MaterialFileEditor.java | 2 +- .../component/editor/impl/model/ModelFileEditor.java | 3 ++- .../component/editor/impl/scene/SceneFileEditor.java | 3 ++- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/ss/editor/config/EditorConfig.java b/src/main/java/com/ss/editor/config/EditorConfig.java index 441347ad..699c8fa8 100644 --- a/src/main/java/com/ss/editor/config/EditorConfig.java +++ b/src/main/java/com/ss/editor/config/EditorConfig.java @@ -16,7 +16,6 @@ import com.ss.editor.util.OpenGLVersion; import com.ss.rlib.logging.Logger; import com.ss.rlib.logging.LoggerManager; -import com.ss.rlib.util.Utils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -848,8 +847,6 @@ public void setOpenGLVersion(@NotNull final OpenGLVersion openGLVersion) { @FromAnyThread public AppSettings getSettings() { - System.out.println("Classloader is from :" + Utils.getRootFolderFromClass(AppSettings.class)); - final GraphicsEnvironment graphicsEnvironment = GraphicsEnvironment.getLocalGraphicsEnvironment(); final GraphicsDevice device = graphicsEnvironment.getDefaultScreenDevice(); final DisplayMode displayMode = device.getDisplayMode(); diff --git a/src/main/java/com/ss/editor/plugin/api/editor/BaseFileEditor.java b/src/main/java/com/ss/editor/plugin/api/editor/BaseFileEditor.java index 13a424fd..6f60ebcd 100644 --- a/src/main/java/com/ss/editor/plugin/api/editor/BaseFileEditor.java +++ b/src/main/java/com/ss/editor/plugin/api/editor/BaseFileEditor.java @@ -13,6 +13,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.IOException; import java.nio.file.Path; import java.util.function.Supplier; @@ -37,7 +38,13 @@ public abstract class BaseFileEditor extends AbstractFile @FXThread public void openFile(@NotNull final Path file) { super.openFile(file); - doOpenFile(file); + + try { + doOpenFile(file); + } catch (final IOException e) { + throw new RuntimeException(e); + } + EXECUTOR_MANAGER.addFXTask(this::loadState); } @@ -73,7 +80,7 @@ protected void loadState() { * @param file the file to open. */ @FXThread - protected void doOpenFile(@NotNull final Path file) { + protected void doOpenFile(@NotNull final Path file) throws IOException { } /** diff --git a/src/main/java/com/ss/editor/ui/component/editor/impl/material/MaterialFileEditor.java b/src/main/java/com/ss/editor/ui/component/editor/impl/material/MaterialFileEditor.java index c89ed875..7d435c95 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/impl/material/MaterialFileEditor.java +++ b/src/main/java/com/ss/editor/ui/component/editor/impl/material/MaterialFileEditor.java @@ -218,7 +218,7 @@ protected void dragOver(@NotNull final DragEvent dragEvent) { @Override @FXThread - protected void doOpenFile(@NotNull final Path file) { + protected void doOpenFile(@NotNull final Path file) throws IOException { super.doOpenFile(file); final Path assetFile = notNull(getAssetFile(file)); diff --git a/src/main/java/com/ss/editor/ui/component/editor/impl/model/ModelFileEditor.java b/src/main/java/com/ss/editor/ui/component/editor/impl/model/ModelFileEditor.java index 99c26f8f..e6a217e7 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/impl/model/ModelFileEditor.java +++ b/src/main/java/com/ss/editor/ui/component/editor/impl/model/ModelFileEditor.java @@ -38,6 +38,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.IOException; import java.nio.file.Path; import java.util.function.Supplier; @@ -150,7 +151,7 @@ private ModelFileEditor() { @Override @FXThread - protected void doOpenFile(@NotNull final Path file) { + protected void doOpenFile(@NotNull final Path file) throws IOException { super.doOpenFile(file); final Path assetFile = notNull(getAssetFile(file), "Asset file for " + file + " can't be null."); diff --git a/src/main/java/com/ss/editor/ui/component/editor/impl/scene/SceneFileEditor.java b/src/main/java/com/ss/editor/ui/component/editor/impl/scene/SceneFileEditor.java index c146a0f9..69f4dd0c 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/impl/scene/SceneFileEditor.java +++ b/src/main/java/com/ss/editor/ui/component/editor/impl/scene/SceneFileEditor.java @@ -44,6 +44,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.IOException; import java.nio.file.Path; import java.util.Objects; import java.util.function.Supplier; @@ -143,7 +144,7 @@ private SceneFileEditor() { @Override @FXThread - protected void doOpenFile(@NotNull final Path file) { + protected void doOpenFile(@NotNull final Path file) throws IOException { super.doOpenFile(file); final Path assetFile = notNull(getAssetFile(file), "Asset file for " + file + " can't be null."); From 1c6db1c3fea0461806fb152f31540a34de000c60 Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Thu, 7 Sep 2017 18:10:05 +0300 Subject: [PATCH 06/50] fixed problem with converting models on windows. --- src/main/java/com/ss/editor/FolderAssetLocator.java | 2 -- .../editor/file/converter/impl/AbstractModelFileConverter.java | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/com/ss/editor/FolderAssetLocator.java b/src/main/java/com/ss/editor/FolderAssetLocator.java index 061c1624..c9c40614 100644 --- a/src/main/java/com/ss/editor/FolderAssetLocator.java +++ b/src/main/java/com/ss/editor/FolderAssetLocator.java @@ -27,8 +27,6 @@ public void setRootPath(@NotNull final String rootPath) { @Override public AssetInfo locate(final AssetManager manager, final AssetKey key) { - System.out.println("Locate " + key.getName() + " in the " + key.getFolder()); - final EditorConfig editorConfig = EditorConfig.getInstance(); final Path currentAsset = editorConfig.getCurrentAsset(); if (currentAsset == null) return null; diff --git a/src/main/java/com/ss/editor/file/converter/impl/AbstractModelFileConverter.java b/src/main/java/com/ss/editor/file/converter/impl/AbstractModelFileConverter.java index b60e5b41..d35185b2 100644 --- a/src/main/java/com/ss/editor/file/converter/impl/AbstractModelFileConverter.java +++ b/src/main/java/com/ss/editor/file/converter/impl/AbstractModelFileConverter.java @@ -84,7 +84,7 @@ private void convertImpl(@NotNull final Path source, @NotNull final ModelConvert final boolean isOverwrite = Files.exists(destination); final Path assetFile = notNull(getAssetFile(source), "Not found asset file for " + source); - final ModelKey modelKey = new ModelKey(assetFile.toString()); + final ModelKey modelKey = new ModelKey(toAssetPath(assetFile)); final AssetManager assetManager = EDITOR.getAssetManager(); final Spatial model = assetManager.loadAsset(modelKey); From ecd47182ce12267e08aec08cd695eb3ffe3ccd21 Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Thu, 7 Sep 2017 18:43:23 +0300 Subject: [PATCH 07/50] fixed the process of saving changes from editor, refactoring. --- .../ss/editor/manager/ResourceManager.java | 41 +++++++-------- .../ss/editor/manager/WorkspaceManager.java | 12 ++--- .../ss/editor/model/workspace/Workspace.java | 51 +++++++++++-------- .../ui/component/asset/AssetComponent.java | 45 +++++++++++----- .../editor/impl/AbstractFileEditor.java | 17 +++---- 5 files changed, 93 insertions(+), 73 deletions(-) diff --git a/src/main/java/com/ss/editor/manager/ResourceManager.java b/src/main/java/com/ss/editor/manager/ResourceManager.java index 7d1d9298..af274078 100644 --- a/src/main/java/com/ss/editor/manager/ResourceManager.java +++ b/src/main/java/com/ss/editor/manager/ResourceManager.java @@ -88,8 +88,7 @@ public class ResourceManager extends EditorThread implements AssetEventListener * * @return the instance */ - @NotNull - public static ResourceManager getInstance() { + public static @NotNull ResourceManager getInstance() { if (instance == null) instance = new ResourceManager(); return instance; } @@ -242,16 +241,16 @@ public synchronized void updateAdditionalEnvs() { * * @return the list. */ - @NotNull - public Array getAdditionalEnvs() { + @FromAnyThread + public @NotNull Array getAdditionalEnvs() { return additionalEnvs; } /** * @return the table with last modify dates. */ - @NotNull - private ObjectDictionary getAssetCacheTable() { + @FromAnyThread + private @NotNull ObjectDictionary getAssetCacheTable() { return assetCacheTable; } @@ -300,22 +299,23 @@ private synchronized void processEvent(@NotNull final CreatedFileEvent event) { /** * @return the list of material definitions in the classpath. */ - @NotNull - private Array getMaterialDefinitionsInClasspath() { + @FromAnyThread + private @NotNull Array getMaterialDefinitionsInClasspath() { return materialDefinitionsInClasspath; } /** * @return the list of resources in the classpath. */ - @NotNull - private Array getResourcesInClasspath() { + @FromAnyThread + private @NotNull Array getResourcesInClasspath() { return resourcesInClasspath; } /** * Prepare classpath resources. */ + @FromAnyThread private void prepareClasspathResources() { final Array materialDefinitionsInClasspath = getMaterialDefinitionsInClasspath(); final Array resourcesInClasspath = getResourcesInClasspath(); @@ -329,8 +329,8 @@ private void prepareClasspathResources() { /** * @return the list of available material definitions in the classpath. */ - @NotNull - private Array getMaterialDefinitions() { + @FromAnyThread + private @NotNull Array getMaterialDefinitions() { return materialDefinitions; } @@ -339,8 +339,8 @@ private Array getMaterialDefinitions() { * * @return the list of an additional classpath. */ - @NotNull - public Array getClassLoaders() { + @FromAnyThread + public @NotNull Array getClassLoaders() { return classLoaders; } @@ -349,9 +349,8 @@ public Array getClassLoaders() { * * @return the list of all available material definitions. */ - @NotNull @FromAnyThread - public synchronized Array getAvailableMaterialDefinitions() { + public synchronized @NotNull Array getAvailableMaterialDefinitions() { final Array result = ArrayFactory.newArray(String.class); addAvailableMaterialDefinitionsTo(result); return result; @@ -420,6 +419,7 @@ public synchronized void reload() { } } + @FromAnyThread private static void registerFiles(@NotNull final Array watchKeys, @NotNull final Path file) { watchKeys.add(get(file, toRegister -> toRegister.register(WATCH_SERVICE, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY))); } @@ -526,9 +526,8 @@ public void run() { * @param path the file. * @return the watch key or null. */ - @Nullable @FromAnyThread - private synchronized WatchKey findWatchKey(@NotNull final Path path) { + private synchronized @Nullable WatchKey findWatchKey(@NotNull final Path path) { final Array watchKeys = getWatchKeys(); return watchKeys.search(path, (watchKey, toCheck) -> watchKey.watchable().equals(toCheck)); } @@ -562,6 +561,7 @@ private synchronized void registerWatchKey(@NotNull final Path path) { /** * Handle refreshing asset folder. */ + @FromAnyThread private void processRefreshAsset() { EXECUTOR_MANAGER.addBackgroundTask(this::reload); } @@ -569,6 +569,7 @@ private void processRefreshAsset() { /** * Handle changing asset folder. */ + @FromAnyThread private void processChangeAsset() { EXECUTOR_MANAGER.addBackgroundTask(this::reload); } @@ -576,8 +577,8 @@ private void processChangeAsset() { /** * @return the list of keys for watching to folders. */ - @NotNull - private Array getWatchKeys() { + @FromAnyThread + private @NotNull Array getWatchKeys() { return watchKeys; } } diff --git a/src/main/java/com/ss/editor/manager/WorkspaceManager.java b/src/main/java/com/ss/editor/manager/WorkspaceManager.java index 8e10998f..56df742b 100644 --- a/src/main/java/com/ss/editor/manager/WorkspaceManager.java +++ b/src/main/java/com/ss/editor/manager/WorkspaceManager.java @@ -42,8 +42,7 @@ public class WorkspaceManager { * * @return the instance */ - @NotNull - public static WorkspaceManager getInstance() { + public static @NotNull WorkspaceManager getInstance() { if (instance == null) instance = new WorkspaceManager(); return instance; } @@ -62,8 +61,7 @@ private WorkspaceManager() { /** * @return the table of workspaces. */ - @NotNull - private ObjectDictionary getWorkspaces() { + private @NotNull ObjectDictionary getWorkspaces() { return workspaces; } @@ -72,9 +70,8 @@ private ObjectDictionary getWorkspaces() { * * @return the current workspace or null. */ - @Nullable @FromAnyThread - public Workspace getCurrentWorkspace() { + public @Nullable Workspace getCurrentWorkspace() { final EditorConfig editorConfig = EditorConfig.getInstance(); final Path currentAsset = editorConfig.getCurrentAsset(); @@ -88,9 +85,8 @@ public Workspace getCurrentWorkspace() { * * @return the workspace. */ - @NotNull @FromAnyThread - private synchronized Workspace getWorkspace(@NotNull final Path assetFolder) { + private synchronized @NotNull Workspace getWorkspace(@NotNull final Path assetFolder) { final ObjectDictionary workspaces = getWorkspaces(); final Workspace exists = workspaces.get(assetFolder); diff --git a/src/main/java/com/ss/editor/model/workspace/Workspace.java b/src/main/java/com/ss/editor/model/workspace/Workspace.java index bd072391..20f6ad80 100644 --- a/src/main/java/com/ss/editor/model/workspace/Workspace.java +++ b/src/main/java/com/ss/editor/model/workspace/Workspace.java @@ -4,6 +4,7 @@ import static com.ss.editor.util.EditorUtil.toAssetPath; import static com.ss.rlib.util.ClassUtils.unsafeCast; import static com.ss.rlib.util.ObjectUtils.notNull; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.manager.WorkspaceManager; import com.ss.editor.ui.component.editor.EditorDescription; import com.ss.editor.ui.component.editor.FileEditor; @@ -19,12 +20,9 @@ import java.io.IOException; import java.io.Serializable; -import java.nio.ByteBuffer; -import java.nio.channels.SeekableByteChannel; import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Path; -import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -93,7 +91,8 @@ public Workspace() { /** * Notify about finished restoring this workspace. */ - public void notifyRestored() { + @FromAnyThread + public synchronized void notifyRestored() { if (openedFiles == null) { openedFiles = new HashMap<>(); @@ -116,7 +115,8 @@ public void notifyRestored() { * * @return the current edited file. */ - public @Nullable String getCurrentEditedFile() { + @FromAnyThread + public synchronized @Nullable String getCurrentEditedFile() { return currentEditedFile; } @@ -125,6 +125,7 @@ public void notifyRestored() { * * @param file the current edited file. */ + @FromAnyThread public synchronized void updateCurrentEditedFile(@Nullable final Path file) { if (file == null) { @@ -139,14 +140,16 @@ public synchronized void updateCurrentEditedFile(@Nullable final Path file) { /** * @return the table with states of editors. */ - private @NotNull Map getEditorStateMap() { + @FromAnyThread + private synchronized @NotNull Map getEditorStateMap() { return notNull(editorStateMap); } /** * @return the list of expanded folders. */ - private @NotNull List getExpandedFolders() { + @FromAnyThread + private synchronized @NotNull List getExpandedFolders() { return notNull(expandedFolders); } @@ -155,6 +158,7 @@ public synchronized void updateCurrentEditedFile(@Nullable final Path file) { * * @return the list of expanded absolute folders. */ + @FromAnyThread public synchronized @NotNull Array getExpandedAbsoluteFolders() { final Array result = ArrayFactory.newArray(Path.class); @@ -171,6 +175,7 @@ public synchronized void updateCurrentEditedFile(@Nullable final Path file) { * * @param folders the folders */ + @FromAnyThread public synchronized void updateExpandedFolders(@NotNull final Array folders) { final List expandedFolders = getExpandedFolders(); @@ -190,6 +195,7 @@ public synchronized void updateExpandedFolders(@NotNull final Array folder * @param stateFactory the state factory. * @return the state of the editor. */ + @FromAnyThread public synchronized @NotNull T getEditorState(@NotNull final Path file, @NotNull final Supplier stateFactory) { @@ -213,6 +219,7 @@ public synchronized void updateExpandedFolders(@NotNull final Array folder * * @param file the file. */ + @FromAnyThread public synchronized void removeEditorState(@NotNull final Path file) { final Path assetFile = getAssetFile(getAssetFolder(), file); @@ -229,7 +236,8 @@ public synchronized void removeEditorState(@NotNull final Path file) { * * @param assetFolder the asset folder of this workspace. */ - public void setAssetFolder(@NotNull final Path assetFolder) { + @FromAnyThread + public synchronized void setAssetFolder(@NotNull final Path assetFolder) { this.assetFolder = assetFolder; } @@ -238,7 +246,8 @@ public void setAssetFolder(@NotNull final Path assetFolder) { * * @return the table of opened files. */ - public @NotNull Map getOpenedFiles() { + @FromAnyThread + public synchronized @NotNull Map getOpenedFiles() { return notNull(openedFiles); } @@ -248,6 +257,7 @@ public void setAssetFolder(@NotNull final Path assetFolder) { * @param file the opened file. * @param fileEditor the editor. */ + @FromAnyThread public synchronized void addOpenedFile(@NotNull final Path file, @NotNull final FileEditor fileEditor) { final Path assetFile = getAssetFile(getAssetFolder(), file); @@ -267,6 +277,7 @@ public synchronized void addOpenedFile(@NotNull final Path file, @NotNull final * * @param file the removed file. */ + @FromAnyThread public synchronized void removeOpenedFile(@NotNull final Path file) { final Path assetFile = getAssetFile(getAssetFolder(), file); @@ -283,13 +294,15 @@ public synchronized void removeOpenedFile(@NotNull final Path file) { * * @return the asset folder of this workspace. */ - public @NotNull Path getAssetFolder() { + @FromAnyThread + public synchronized @NotNull Path getAssetFolder() { return notNull(assetFolder); } /** * Increase a counter of changes. */ + @FromAnyThread private void incrementChanges() { changes.incrementAndGet(); } @@ -297,7 +310,8 @@ private void incrementChanges() { /** * Clear this workspace. */ - public void clear() { + @FromAnyThread + public synchronized void clear() { getOpenedFiles().clear(); } @@ -306,7 +320,8 @@ public void clear() { * * @param force the force */ - public void save(final boolean force) { + @FromAnyThread + public synchronized void save(final boolean force) { if (!force && changes.get() == 0) return; final Path assetFolder = getAssetFolder(); @@ -342,16 +357,8 @@ public void save(final boolean force) { changes.set(0); - final byte[] serialize = EditorUtil.serialize(this); - - try (final SeekableByteChannel channel = Files.newByteChannel(workspaceFile, StandardOpenOption.WRITE)) { - - final ByteBuffer buffer = ByteBuffer.wrap(serialize); - buffer.position(serialize.length); - buffer.flip(); - - channel.write(buffer); - + try { + Files.write(workspaceFile, EditorUtil.serialize(this)); } catch (final IOException e) { LOGGER.warning(e); } diff --git a/src/main/java/com/ss/editor/ui/component/asset/AssetComponent.java b/src/main/java/com/ss/editor/ui/component/asset/AssetComponent.java index 00ca1662..b0e642bb 100644 --- a/src/main/java/com/ss/editor/ui/component/asset/AssetComponent.java +++ b/src/main/java/com/ss/editor/ui/component/asset/AssetComponent.java @@ -1,6 +1,8 @@ package com.ss.editor.ui.component.asset; import static com.ss.rlib.util.ObjectUtils.notNull; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.config.EditorConfig; import com.ss.editor.manager.ExecutorManager; import com.ss.editor.manager.WorkspaceManager; @@ -35,9 +37,15 @@ public class AssetComponent extends VBox implements ScreenComponent { @NotNull private static final String COMPONENT_ID = "AssetComponent"; + /** + * The executor manager. + */ @NotNull private static final ExecutorManager EXECUTOR_MANAGER = ExecutorManager.getInstance(); + /** + * The event manager. + */ @NotNull private static final FXEventManager FX_EVENT_MANAGER = FXEventManager.getInstance(); @@ -83,14 +91,15 @@ public AssetComponent() { * * @return the list of waited files to select. */ - @NotNull - private Array getWaitedFilesToSelect() { + @FromAnyThread + private @NotNull Array getWaitedFilesToSelect() { return waitedFilesToSelect; } /** * Handle request for selection a file. */ + @FXThread private void processEvent(@NotNull final RequestSelectFileEvent event) { final Path file = event.getFile(); @@ -110,6 +119,7 @@ private void processEvent(@NotNull final RequestSelectFileEvent event) { /** * Handle a created file. */ + @FXThread private void processEvent(@NotNull final CreatedFileEvent event) { final Path file = event.getFile(); @@ -127,6 +137,7 @@ private void processEvent(@NotNull final CreatedFileEvent event) { /** * Handle a deleted file. */ + @FXThread private void processEvent(@NotNull final DeletedFileEvent event) { final Path file = event.getFile(); @@ -143,19 +154,15 @@ private void processEvent(@NotNull final DeletedFileEvent event) { /** * Handle changing an asset folder. */ + @FXThread private void processChangeAsset() { - - final EditorConfig editorConfig = EditorConfig.getInstance(); - final Path currentAsset = editorConfig.getCurrentAsset(); - if (currentAsset == null) return; - - final ResourceTree resourceTree = getResourceTree(); - resourceTree.fill(currentAsset); + loadAssetFolder(); } /** * Handle refreshing. */ + @FXThread private void processRefresh() { final ResourceTree resourceTree = getResourceTree(); resourceTree.refresh(); @@ -164,6 +171,7 @@ private void processRefresh() { /** * Create components. */ + @FXThread private void createComponents() { setIgnoreExpanded(true); @@ -184,6 +192,7 @@ private void createComponents() { /** * Handle changing loading state of the tree. */ + @FXThread private void handleTreeLoading(@NotNull final Boolean finished) { final WorkspaceManager workspaceManager = WorkspaceManager.getInstance(); @@ -206,6 +215,7 @@ private void handleTreeLoading(@NotNull final Boolean finished) { * * @return true if the expand listener is ignored. */ + @FromAnyThread private boolean isIgnoreExpanded() { return ignoreExpanded; } @@ -215,6 +225,7 @@ private boolean isIgnoreExpanded() { * * @param ignoreExpanded the flag for ignoring expand changes. */ + @FromAnyThread private void setIgnoreExpanded(final boolean ignoreExpanded) { this.ignoreExpanded = ignoreExpanded; } @@ -222,6 +233,7 @@ private void setIgnoreExpanded(final boolean ignoreExpanded) { /** * Handle changes count of expanded folders. */ + @FXThread private void updateExpanded(final int count, final ResourceTree tree) { if (isIgnoreExpanded()) return; @@ -244,26 +256,33 @@ private void updateExpanded(final int count, final ResourceTree tree) { /** * @return the toolbar of this component. */ - @NotNull - private AssetBarComponent getBarComponent() { + @FXThread + private @NotNull AssetBarComponent getBarComponent() { return notNull(barComponent); } /** * @return the resource tree. */ - @NotNull - private ResourceTree getResourceTree() { + @FXThread + private @NotNull ResourceTree getResourceTree() { return notNull(resourceTree); } @Override + @FromAnyThread public String getComponentId() { return COMPONENT_ID; } @Override + @FXThread public void notifyFinishBuild() { + loadAssetFolder(); + } + + @FXThread + private void loadAssetFolder() { final EditorConfig editorConfig = EditorConfig.getInstance(); final Path currentAsset = editorConfig.getCurrentAsset(); diff --git a/src/main/java/com/ss/editor/ui/component/editor/impl/AbstractFileEditor.java b/src/main/java/com/ss/editor/ui/component/editor/impl/AbstractFileEditor.java index 9bce566f..5b8a18ba 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/impl/AbstractFileEditor.java +++ b/src/main/java/com/ss/editor/ui/component/editor/impl/AbstractFileEditor.java @@ -1,8 +1,7 @@ package com.ss.editor.ui.component.editor.impl; import static com.ss.rlib.util.ObjectUtils.notNull; -import static java.nio.file.StandardCopyOption.ATOMIC_MOVE; -import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; +import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING; import com.jme3.math.Vector3f; import com.ss.editor.Editor; import com.ss.editor.JFXApplication; @@ -47,7 +46,9 @@ import org.jetbrains.annotations.Nullable; import java.io.IOException; -import java.nio.file.*; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; import java.time.Duration; import java.time.LocalTime; import java.util.function.Consumer; @@ -366,17 +367,13 @@ public void save(@Nullable final Consumer<@NotNull FileEditor> callback) { final Path editFile = getEditFile(); doSave(tempFile); - try { - Files.move(tempFile, editFile, REPLACE_EXISTING, ATOMIC_MOVE); - } catch (final AtomicMoveNotSupportedException e) { - Files.move(tempFile, editFile, REPLACE_EXISTING); - } catch (final AccessDeniedException e) { - Files.copy(tempFile, editFile, StandardCopyOption.REPLACE_EXISTING); + try (final OutputStream out = Files.newOutputStream(editFile, TRUNCATE_EXISTING)) { + Files.copy(tempFile, out); + } finally { FileUtils.delete(tempFile); } } catch (final IOException e) { - FileUtils.delete(tempFile); LOGGER.warning(this, e); EXECUTOR_MANAGER.addFXTask(this::notifyFinishSaving); } finally { From b25241b6fd11c7c4f15d51466ed6748faf6d675a Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Thu, 7 Sep 2017 21:09:47 +0300 Subject: [PATCH 08/50] added new label. --- src/main/resources/messages/messages.properties | 1 + src/main/resources/messages/messages_de.properties | 1 + src/main/resources/messages/messages_ru.properties | 1 + 3 files changed, 3 insertions(+) diff --git a/src/main/resources/messages/messages.properties b/src/main/resources/messages/messages.properties index d13573a3..5cdcc976 100644 --- a/src/main/resources/messages/messages.properties +++ b/src/main/resources/messages/messages.properties @@ -215,6 +215,7 @@ ModelNodeTreeActionSpotLight=Spot light ModelNodeTreeActionAnimationPlay=Play ModelNodeTreeActionAnimationPlaySettings=Play settings ModelNodeTreeActionAnimationStop=Stop +ModelNodeTreeActionAnimationPause=Pause ModelNodeTreeActionAnimationManualExtractSubAnimation=Extract sub-animation manually ModelNodeTreeActionCreateAudioNode=Audio node ModelNodeTreeActionAudioPlay=Play diff --git a/src/main/resources/messages/messages_de.properties b/src/main/resources/messages/messages_de.properties index a6187954..8db421cf 100644 --- a/src/main/resources/messages/messages_de.properties +++ b/src/main/resources/messages/messages_de.properties @@ -215,6 +215,7 @@ ModelNodeTreeActionSpotLight=Projizierende Lichtquelle ModelNodeTreeActionAnimationPlay=Abspielen ModelNodeTreeActionAnimationPlaySettings=Abspieleinstellungen ModelNodeTreeActionAnimationStop=Stoppen +ModelNodeTreeActionAnimationPause=Pause ModelNodeTreeActionAnimationManualExtractSubAnimation=Manuelles Entnehmen der Animation ModelNodeTreeActionCreateAudioNode=Audioknoten ModelNodeTreeActionAudioPlay=Abspielen diff --git a/src/main/resources/messages/messages_ru.properties b/src/main/resources/messages/messages_ru.properties index b09f0a06..bd595971 100644 --- a/src/main/resources/messages/messages_ru.properties +++ b/src/main/resources/messages/messages_ru.properties @@ -217,6 +217,7 @@ ModelNodeTreeActionSpotLight=Световое пятно ModelNodeTreeActionAnimationPlay=Воспроизвести ModelNodeTreeActionAnimationPlaySettings=Настройки воспроизведения ModelNodeTreeActionAnimationStop=Остановить +ModelNodeTreeActionAnimationPause=Пауза ModelNodeTreeActionAnimationManualExtractSubAnimation=Извлечь вручную анимацию ModelNodeTreeActionCreateAudioNode=Аудио узел ModelNodeTreeActionAudioPlay=Проиграть From 37aa32c5129b76c2fc4ba7d7faffbf825b83e36f Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Thu, 7 Sep 2017 21:09:58 +0300 Subject: [PATCH 09/50] refactoring actions. --- src/main/java/com/ss/editor/Messages.java | 5 +++ .../model/tree/action/AddUserDataAction.java | 2 ++ .../tree/action/CreateEditableSkyAction.java | 13 ++----- .../model/tree/action/CreateNodeAction.java | 16 +++------ .../model/tree/action/CreateSkyAction.java | 19 ++++------ .../model/tree/action/LinkModelAction.java | 17 ++++----- .../model/tree/action/LoadModelAction.java | 19 +++++----- .../action/MakeAsEmbeddedMaterialAction.java | 2 ++ .../tree/action/OptimizeGeometryAction.java | 16 +++------ .../tree/action/RemoveControlAction.java | 16 +++------ .../model/tree/action/RemoveLightAction.java | 16 +++------ .../model/tree/action/RemoveNodeAction.java | 20 ++++------- .../model/tree/action/RenameNodeAction.java | 20 ++++------- .../tree/action/SaveAsMaterialAction.java | 3 ++ .../tree/action/TangentGeneratorAction.java | 16 +++------ .../ManualExtractSubAnimationAction.java | 16 +++------ .../animation/PauseAnimationAction.java | 22 +++++------- .../action/animation/PlayAnimationAction.java | 21 +++++------ .../action/animation/PlaySettingsAction.java | 16 +++------ .../animation/RemoveAnimationAction.java | 16 +++------ .../action/animation/StopAnimationAction.java | 16 +++------ .../action/audio/CreateAudioNodeAction.java | 16 +++------ .../action/audio/PlayAudioNodeAction.java | 24 +++++-------- .../action/audio/StopAudioNodeAction.java | 17 ++++----- .../control/AbstractCreateControlAction.java | 12 ++----- .../control/CreateCustomControlAction.java | 2 ++ .../control/CreateMotionControlAction.java | 19 ++++------ .../physics/CreateCharacterControlAction.java | 20 +++++------ .../CreateKinematicRagdollControlAction.java | 22 +++++------- .../physics/CreateRigidBodyControlAction.java | 19 ++++------ .../CreateStaticRigidBodyControlAction.java | 20 +++++------ .../physics/ReactivatePhysicsControl.java | 2 ++ .../vehicle/CreateVehicleControlAction.java | 19 ++++------ .../vehicle/CreateVehicleWheelAction.java | 2 ++ .../vehicle/RemoveVehicleWheelAction.java | 2 ++ .../AbstractCreateGeometryAction.java | 12 ++----- .../tree/action/geometry/CreateBoxAction.java | 19 ++++------ .../action/geometry/CreateQuadAction.java | 19 ++++------ .../action/geometry/CreateSphereAction.java | 19 ++++------ .../action/geometry/GenerateLoDAction.java | 16 +++------ .../light/AbstractCreateLightAction.java | 12 ++----- .../light/CreateAmbientLightAction.java | 19 ++++------ .../light/CreateDirectionLightAction.java | 19 ++++------ .../action/light/CreatePointLightAction.java | 20 +++++------ .../action/light/CreateSpotLightAction.java | 19 ++++------ .../emitter/CreateParticleEmitterAction.java | 2 ++ .../emitter/ResetParticleEmittersAction.java | 2 ++ ...bstractCreateParticleInfluencerAction.java | 18 ++++------ ...CreateDefaultParticleInfluencerAction.java | 18 ++++------ .../CreateEmptyParticleInfluencerAction.java | 18 ++++------ .../CreateRadialParticleInfluencerAction.java | 18 ++++------ .../AbstractCreateShapeEmitterAction.java | 32 +++++++---------- .../shape/CreateBoxShapeEmitterAction.java | 27 ++++++-------- ...reateMeshConvexHullShapeEmitterAction.java | 15 +++----- .../CreateMeshFaceShapeEmitterAction.java | 18 ++++------ .../CreateMeshVertexShapeEmitterAction.java | 36 ++++++++----------- .../shape/CreatePointShapeEmitterAction.java | 32 +++++++---------- .../shape/CreateSphereShapeEmitterAction.java | 32 +++++++---------- .../CreateToneg0dParticleEmitterAction.java | 2 ++ ...reateToneg0dSoftParticleEmitterAction.java | 2 ++ ...bstractCreateParticleInfluencerAction.java | 21 +++++------ .../CreateAlphaParticleInfluencerAction.java | 20 ++++------- .../CreateColorParticleInfluencerAction.java | 20 ++++------- ...teDestinationParticleInfluencerAction.java | 20 ++++------- ...CreateGravityParticleInfluencerAction.java | 20 ++++------- ...CreateImpulseParticleInfluencerAction.java | 20 ++++------- ...CreatePhysicsParticleInfluencerAction.java | 20 ++++------- ...adialVelocityParticleInfluencerAction.java | 20 ++++------- ...reateRotationParticleInfluencerAction.java | 20 ++++------- .../CreateSizeParticleInfluencerAction.java | 21 +++++------ .../CreateSpriteParticleInfluencerAction.java | 20 ++++------- .../RemoveParticleInfluencerAction.java | 2 ++ .../AbstractCreateParticleMeshAction.java | 1 + .../CreateImpostorParticleMeshAction.java | 3 ++ .../mesh/CreatePointParticleMeshAction.java | 3 ++ .../mesh/CreateQuadParticleMeshAction.java | 3 ++ .../mesh/LoadModelParticlesMeshAction.java | 2 ++ .../AbstractCreateShapeEmitterAction.java | 30 +++++++--------- .../shape/CreateBoxShapeEmitterAction.java | 27 ++++++-------- .../CreateCylinderShapeEmitterAction.java | 29 +++++++-------- .../shape/CreateDomeShapeEmitterAction.java | 29 +++++++-------- .../shape/CreateQuadShapeEmitterAction.java | 27 ++++++-------- .../shape/CreateSphereShapeEmitterAction.java | 32 +++++++---------- .../shape/CreateTorusShapeEmitterAction.java | 29 +++++++-------- .../CreateTriangleShapeEmitterAction.java | 29 +++++++-------- .../shape/LoadModelShapeEmitterAction.java | 2 ++ .../shape/AbstractCreateShapeAction.java | 27 ++++++-------- .../shape/CreateBoxCollisionShapeAction.java | 25 ++++++------- .../CreateCapsuleCollisionShapeAction.java | 27 ++++++-------- .../shape/CreateConeCollisionShapeAction.java | 32 +++++++---------- .../CreateCylinderCollisionShapeAction.java | 32 +++++++---------- .../CreateSphereCollisionShapeAction.java | 32 +++++++---------- .../shape/GenerateCollisionShapeAction.java | 30 +++++++--------- .../action/scene/CreateSceneLayerAction.java | 26 +++++--------- .../action/scene/RemoveSceneLayerAction.java | 18 ++++------ .../action/terrain/CreateTerrainAction.java | 2 ++ .../tree/action/AbstractNodeAction.java | 4 +++ 97 files changed, 650 insertions(+), 1024 deletions(-) diff --git a/src/main/java/com/ss/editor/Messages.java b/src/main/java/com/ss/editor/Messages.java index c15c5fb5..b6ed43c4 100644 --- a/src/main/java/com/ss/editor/Messages.java +++ b/src/main/java/com/ss/editor/Messages.java @@ -820,6 +820,10 @@ public class Messages { * The constant MODEL_NODE_TREE_ACTION_ANIMATION_STOP. */ public static final String MODEL_NODE_TREE_ACTION_ANIMATION_STOP; + /** + * The constant MODEL_NODE_TREE_ACTION_ANIMATION_PAUSE. + */ + public static final String MODEL_NODE_TREE_ACTION_ANIMATION_PAUSE; /** * The constant MODEL_NODE_TREE_ACTION_ANIMATION_MANUAL_EXTRAXT_SUB_ANIMATION. */ @@ -2639,6 +2643,7 @@ public class Messages { MODEL_NODE_TREE_ACTION_ANIMATION_PLAY = bundle.getString("ModelNodeTreeActionAnimationPlay"); MODEL_NODE_TREE_ACTION_ANIMATION_PLAY_SETTINGS = bundle.getString("ModelNodeTreeActionAnimationPlaySettings"); MODEL_NODE_TREE_ACTION_ANIMATION_STOP = bundle.getString("ModelNodeTreeActionAnimationStop"); + MODEL_NODE_TREE_ACTION_ANIMATION_PAUSE = bundle.getString("ModelNodeTreeActionAnimationPause"); MODEL_NODE_TREE_ACTION_ANIMATION_MANUAL_EXTRAXT_SUB_ANIMATION = bundle.getString("ModelNodeTreeActionAnimationManualExtractSubAnimation"); MODEL_NODE_TREE_ACTION_CREATE_AUDIO_NODE = bundle.getString("ModelNodeTreeActionCreateAudioNode"); MODEL_NODE_TREE_ACTION_AUDIO_PLAY = bundle.getString("ModelNodeTreeActionAudioPlay"); diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/AddUserDataAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/AddUserDataAction.java index 0e052508..ed052bd8 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/AddUserDataAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/AddUserDataAction.java @@ -75,11 +75,13 @@ public AddUserDataAction(@NotNull final NodeTree nodeTree, @NotNull final Tre super(nodeTree, node); } + @FXThread @Override protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_ADD_USER_DATA; } + @FXThread @Override protected @Nullable Image getIcon() { return Icons.ADD_12; diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/CreateEditableSkyAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/CreateEditableSkyAction.java index c5f80d9d..d5d29085 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/CreateEditableSkyAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/CreateEditableSkyAction.java @@ -15,26 +15,19 @@ */ public class CreateEditableSkyAction extends CreateSkyAction { - /** - * Instantiates a new Create sky action. - * - * @param nodeTree the node tree - * @param node the node - */ public CreateEditableSkyAction(final @NotNull NodeTree nodeTree, final @NotNull TreeNode node) { super(nodeTree, node); } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_CREATE_EDITABLE_SKY; } - @NotNull @Override @FXThread - protected CreateSkyDialog createDialog() { + protected @NotNull CreateSkyDialog createDialog() { return new CreateEditableSkyDialog(getNode(), getNodeTree()); } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/CreateNodeAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/CreateNodeAction.java index 096f170b..54f735be 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/CreateNodeAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/CreateNodeAction.java @@ -24,30 +24,24 @@ */ public class CreateNodeAction extends AbstractNodeAction { - /** - * Instantiates a new Create node action. - * - * @param nodeTree the node tree - * @param node the node - */ public CreateNodeAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.NODE_16; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_CREATE_NODE; } - @FXThread @Override + @FXThread protected void process() { super.process(); diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/CreateSkyAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/CreateSkyAction.java index 83fd41ac..df9a03d0 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/CreateSkyAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/CreateSkyAction.java @@ -19,30 +19,24 @@ */ public class CreateSkyAction extends AbstractNodeAction { - /** - * Instantiates a new Create sky action. - * - * @param nodeTree the node tree - * @param node the node - */ public CreateSkyAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.SKY_16; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_CREATE_SKY; } - @FXThread @Override + @FXThread protected void process() { super.process(); @@ -50,9 +44,8 @@ protected void process() { dialog.show(); } - @NotNull @FXThread - protected CreateSkyDialog createDialog() { + protected @NotNull CreateSkyDialog createDialog() { return new CreateSkyDialog(getNode(), getNodeTree()); } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/LinkModelAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/LinkModelAction.java index da854897..41096382 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/LinkModelAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/LinkModelAction.java @@ -41,35 +41,31 @@ */ public class LinkModelAction extends AbstractNodeAction { + @NotNull private static final Predicate> ACTION_TESTER = type -> type == NewFileAction.class || type == DeleteFileAction.class || type == RenameFileAction.class; + @NotNull private static final Array MODEL_EXTENSIONS = ArrayFactory.newArray(String.class); static { MODEL_EXTENSIONS.add(FileExtensions.JME_OBJECT); } - /** - * Instantiates a new Link model action. - * - * @param nodeTree the node tree - * @param node the node - */ public LinkModelAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.LINK_FILE_16; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_LINK_MODEL; } @@ -85,6 +81,7 @@ protected void process() { * * @param file the file */ + @FXThread protected void processOpen(@NotNull final Path file) { final NodeTree nodeTree = getNodeTree(); diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/LoadModelAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/LoadModelAction.java index 57dea7bc..24e9600a 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/LoadModelAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/LoadModelAction.java @@ -38,40 +38,36 @@ */ public class LoadModelAction extends AbstractNodeAction { + @NotNull private static final Predicate> ACTION_TESTER = type -> type == NewFileAction.class || type == DeleteFileAction.class || type == RenameFileAction.class; + @NotNull private static final Array MODEL_EXTENSIONS = ArrayFactory.newArray(String.class); static { MODEL_EXTENSIONS.add(FileExtensions.JME_OBJECT); } - /** - * Instantiates a new Load model action. - * - * @param nodeTree the node tree - * @param node the node - */ public LoadModelAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.OPEN_FILE_16; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_LOAD_MODEL; } - @FXThread @Override + @FXThread protected void process() { super.process(); UIUtils.openFileAssetDialog(this::processOpen, MODEL_EXTENSIONS, ACTION_TESTER); @@ -82,6 +78,7 @@ protected void process() { * * @param file the file */ + @FXThread protected void processOpen(@NotNull final Path file) { final NodeTree nodeTree = getNodeTree(); diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/MakeAsEmbeddedMaterialAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/MakeAsEmbeddedMaterialAction.java index ed02bf86..b748ef1d 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/MakeAsEmbeddedMaterialAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/MakeAsEmbeddedMaterialAction.java @@ -27,11 +27,13 @@ public MakeAsEmbeddedMaterialAction(@NotNull final NodeTree nodeTree, @NotNul } @Override + @FXThread protected @Nullable Image getIcon() { return Icons.INFLUENCER_16; } @Override + @FXThread protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_MAKE_EMBEDDED; } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/OptimizeGeometryAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/OptimizeGeometryAction.java index 3474334a..6cee5a40 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/OptimizeGeometryAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/OptimizeGeometryAction.java @@ -23,30 +23,24 @@ */ public class OptimizeGeometryAction extends AbstractNodeAction { - /** - * Instantiates a new Optimize geometry action. - * - * @param nodeTree the node tree - * @param node the node - */ public OptimizeGeometryAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.INFLUENCER_16; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_OPTIMIZE_GEOMETRY; } - @FXThread @Override + @FXThread protected void process() { super.process(); diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/RemoveControlAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/RemoveControlAction.java index 14f47a57..3da65618 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/RemoveControlAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/RemoveControlAction.java @@ -24,30 +24,24 @@ */ public class RemoveControlAction extends AbstractNodeAction { - /** - * Instantiates a new Remove control action. - * - * @param nodeTree the node tree - * @param node the node - */ public RemoveControlAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_REMOVE; } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.REMOVE_12; } - @FXThread @Override + @FXThread protected void process() { super.process(); diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/RemoveLightAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/RemoveLightAction.java index 67343706..55c6cee8 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/RemoveLightAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/RemoveLightAction.java @@ -24,30 +24,24 @@ */ public class RemoveLightAction extends AbstractNodeAction { - /** - * Instantiates a new Remove light action. - * - * @param nodeTree the node tree - * @param node the node - */ public RemoveLightAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.REMOVE_12; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_REMOVE; } - @FXThread @Override + @FXThread protected void process() { super.process(); diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/RemoveNodeAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/RemoveNodeAction.java index 40d2c9e5..f2546ca3 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/RemoveNodeAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/RemoveNodeAction.java @@ -11,12 +11,10 @@ import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.action.AbstractNodeAction; import com.ss.editor.ui.control.tree.node.TreeNode; - +import javafx.scene.image.Image; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import javafx.scene.image.Image; - /** * The action to remove a node from model. * @@ -24,30 +22,24 @@ */ public class RemoveNodeAction extends AbstractNodeAction { - /** - * Instantiates a new Remove node action. - * - * @param nodeTree the node tree - * @param node the node - */ public RemoveNodeAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_REMOVE; } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.REMOVE_12; } - @FXThread @Override + @FXThread protected void process() { super.process(); diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/RenameNodeAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/RenameNodeAction.java index b431ae6d..6152b45c 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/RenameNodeAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/RenameNodeAction.java @@ -7,12 +7,10 @@ import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.action.AbstractNodeAction; import com.ss.editor.ui.control.tree.node.TreeNode; - +import javafx.scene.image.Image; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import javafx.scene.image.Image; - /** * The action to rename a model node. * @@ -20,30 +18,24 @@ */ public class RenameNodeAction extends AbstractNodeAction { - /** - * Instantiates a new Rename node action. - * - * @param nodeTree the node tree - * @param node the node - */ public RenameNodeAction(@NotNull final NodeTree nodeTree, @NotNull TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.EDIT_16; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_RENAME; } - @FXThread @Override + @FXThread protected void process() { super.process(); final NodeTree nodeTree = getNodeTree(); diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/SaveAsMaterialAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/SaveAsMaterialAction.java index b016f162..b07d155a 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/SaveAsMaterialAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/SaveAsMaterialAction.java @@ -49,11 +49,13 @@ public SaveAsMaterialAction(@NotNull final NodeTree nodeTree, @NotNull final } @Override + @FXThread protected @Nullable Image getIcon() { return Icons.SAVE_16; } @Override + @FXThread protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_SAVE_AS; } @@ -70,6 +72,7 @@ protected void process() { * * @param file the file to save */ + @FXThread private void processSave(@NotNull final Path file) { final TreeNode node = getNode(); diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/TangentGeneratorAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/TangentGeneratorAction.java index 0c880f23..127335eb 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/TangentGeneratorAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/TangentGeneratorAction.java @@ -19,30 +19,24 @@ */ public class TangentGeneratorAction extends AbstractNodeAction { - /** - * Instantiates a new Tangent generator action. - * - * @param nodeTree the node tree - * @param node the node - */ public TangentGeneratorAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_TANGENT_GENERATOR; } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.MESH_16; } - @FXThread @Override + @FXThread protected void process() { super.process(); final GenerateTangentsDialog dialog = new GenerateTangentsDialog(getNodeTree(), getNode()); diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/animation/ManualExtractSubAnimationAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/animation/ManualExtractSubAnimationAction.java index 7f6dacc5..0dfcde01 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/animation/ManualExtractSubAnimationAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/animation/ManualExtractSubAnimationAction.java @@ -20,30 +20,24 @@ */ public class ManualExtractSubAnimationAction extends AbstractNodeAction { - /** - * Instantiates a new Manual extract sub animation action. - * - * @param nodeTree the node tree - * @param node the node - */ public ManualExtractSubAnimationAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_ANIMATION_MANUAL_EXTRAXT_SUB_ANIMATION; } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.EXTRACT_16; } - @FXThread @Override + @FXThread protected void process() { super.process(); final NodeTree nodeTree = getNodeTree(); diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/animation/PauseAnimationAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/animation/PauseAnimationAction.java index 5d1dfdae..43b67943 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/animation/PauseAnimationAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/animation/PauseAnimationAction.java @@ -2,12 +2,13 @@ import com.jme3.animation.AnimChannel; import com.jme3.animation.AnimControl; +import com.ss.editor.Messages; import com.ss.editor.annotation.FXThread; import com.ss.editor.model.undo.editor.ModelChangeConsumer; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.model.node.control.anim.AnimationTreeNode; -import com.ss.editor.ui.control.tree.action.AbstractNodeAction; import com.ss.editor.ui.control.tree.NodeTree; +import com.ss.editor.ui.control.tree.action.AbstractNodeAction; import com.ss.editor.ui.control.tree.node.TreeNode; import javafx.scene.image.Image; import org.jetbrains.annotations.NotNull; @@ -20,30 +21,25 @@ */ public class PauseAnimationAction extends AbstractNodeAction { - /** - * Instantiates a new Pause animation action. - * - * @param nodeTree the node tree - * @param node the node - */ public PauseAnimationAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @NotNull + @Override - protected String getName() { - return "Pause"; //Messages.MODEL_NODE_TREE_ACTION_ANIMATION_STOP; + @FXThread + protected @NotNull String getName() { + return Messages.MODEL_NODE_TREE_ACTION_ANIMATION_PAUSE; } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.PAUSE_16; } - @FXThread @Override + @FXThread protected void process() { super.process(); diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/animation/PlayAnimationAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/animation/PlayAnimationAction.java index 1c79c416..edb59e44 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/animation/PlayAnimationAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/animation/PlayAnimationAction.java @@ -3,12 +3,13 @@ import com.jme3.animation.*; import com.ss.editor.Messages; import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.JMEThread; import com.ss.editor.model.undo.editor.ModelChangeConsumer; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.model.node.control.anim.AnimationControlTreeNode; import com.ss.editor.ui.control.model.node.control.anim.AnimationTreeNode; -import com.ss.editor.ui.control.tree.action.AbstractNodeAction; import com.ss.editor.ui.control.tree.NodeTree; +import com.ss.editor.ui.control.tree.action.AbstractNodeAction; import com.ss.editor.ui.control.tree.node.TreeNode; import com.ss.rlib.util.StringUtils; import javafx.scene.image.Image; @@ -22,30 +23,24 @@ */ public class PlayAnimationAction extends AbstractNodeAction implements AnimEventListener { - /** - * Instantiates a new Play animation action. - * - * @param nodeTree the node tree - * @param node the node - */ public PlayAnimationAction(final NodeTree nodeTree, final TreeNode node) { super(nodeTree, node); } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_ANIMATION_PLAY; } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.PLAY_16; } - @FXThread @Override + @FXThread protected void process() { super.process(); @@ -81,6 +76,7 @@ protected void process() { } @Override + @JMEThread public void onAnimCycleDone(@NotNull final AnimControl control, @NotNull final AnimChannel channel, @NotNull final String animName) { @@ -97,6 +93,7 @@ public void onAnimCycleDone(@NotNull final AnimControl control, @NotNull final A } @Override + @JMEThread public void onAnimChange(@NotNull final AnimControl control, @NotNull final AnimChannel channel, @NotNull final String animName) { } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/animation/PlaySettingsAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/animation/PlaySettingsAction.java index ce0eef87..25650df7 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/animation/PlaySettingsAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/animation/PlaySettingsAction.java @@ -31,30 +31,24 @@ public class PlaySettingsAction extends AbstractNodeAction @NotNull private static final String PROPERTY_SPEED = "speed"; - /** - * Instantiates a new Play settings action. - * - * @param nodeTree the node tree - * @param node the node - */ public PlaySettingsAction(@NotNull final NodeTree nodeTree, @NotNull final AnimationControlTreeNode node) { super(nodeTree, node); } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_ANIMATION_PLAY_SETTINGS; } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.SETTINGS_16; } - @FXThread @Override + @FXThread protected void process() { super.process(); diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/animation/RemoveAnimationAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/animation/RemoveAnimationAction.java index 0d0ed469..4b79e75d 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/animation/RemoveAnimationAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/animation/RemoveAnimationAction.java @@ -22,30 +22,24 @@ */ public class RemoveAnimationAction extends AbstractNodeAction { - /** - * Instantiates a new Remove animation action. - * - * @param nodeTree the node tree - * @param node the node - */ public RemoveAnimationAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_REMOVE; } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.REMOVE_12; } - @FXThread @Override + @FXThread protected void process() { super.process(); diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/animation/StopAnimationAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/animation/StopAnimationAction.java index 4cdf2f91..49826d20 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/animation/StopAnimationAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/animation/StopAnimationAction.java @@ -24,30 +24,24 @@ */ public class StopAnimationAction extends AbstractNodeAction { - /** - * Instantiates a new Stop animation action. - * - * @param nodeTree the node tree - * @param node the node - */ public StopAnimationAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_ANIMATION_STOP; } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.STOP_16; } - @FXThread @Override + @FXThread protected void process() { super.process(); diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/audio/CreateAudioNodeAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/audio/CreateAudioNodeAction.java index 757c1fab..31606e13 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/audio/CreateAudioNodeAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/audio/CreateAudioNodeAction.java @@ -25,30 +25,24 @@ */ public class CreateAudioNodeAction extends AbstractNodeAction { - /** - * Instantiates a new Create audio node action. - * - * @param nodeTree the node tree - * @param node the node - */ public CreateAudioNodeAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.AUDIO_16; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_CREATE_AUDIO_NODE; } - @FXThread @Override + @FXThread protected void process() { super.process(); diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/audio/PlayAudioNodeAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/audio/PlayAudioNodeAction.java index a353f7e5..d2ba0377 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/audio/PlayAudioNodeAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/audio/PlayAudioNodeAction.java @@ -8,17 +8,15 @@ import com.ss.editor.annotation.FXThread; import com.ss.editor.model.undo.editor.ModelChangeConsumer; import com.ss.editor.ui.Icons; -import com.ss.editor.ui.control.tree.action.AbstractNodeAction; +import com.ss.editor.ui.control.model.node.spatial.AudioTreeNode; import com.ss.editor.ui.control.tree.NodeTree; +import com.ss.editor.ui.control.tree.action.AbstractNodeAction; import com.ss.editor.ui.control.tree.node.TreeNode; -import com.ss.editor.ui.control.model.node.spatial.AudioTreeNode; import com.ss.editor.util.AudioNodeUtils; - +import javafx.scene.image.Image; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import javafx.scene.image.Image; - /** * The action to play an audio node. * @@ -26,30 +24,24 @@ */ public class PlayAudioNodeAction extends AbstractNodeAction { - /** - * Instantiates a new Play audio node action. - * - * @param nodeTree the node tree - * @param node the node - */ public PlayAudioNodeAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.PLAY_16; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_AUDIO_PLAY; } - @FXThread @Override + @FXThread protected void process() { super.process(); diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/audio/StopAudioNodeAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/audio/StopAudioNodeAction.java index 3b7f4e30..eb8ab69e 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/audio/StopAudioNodeAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/audio/StopAudioNodeAction.java @@ -22,30 +22,25 @@ */ public class StopAudioNodeAction extends AbstractNodeAction { - /** - * Instantiates a new Stop audio node action. - * - * @param nodeTree the node tree - * @param node the node - */ public StopAudioNodeAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.STOP_16; } - @NotNull + @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_AUDIO_STOP; } - @FXThread @Override + @FXThread protected void process() { super.process(); diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/control/AbstractCreateControlAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/control/AbstractCreateControlAction.java index 4926808f..1cb44983 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/control/AbstractCreateControlAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/control/AbstractCreateControlAction.java @@ -18,18 +18,12 @@ */ public abstract class AbstractCreateControlAction extends AbstractNodeAction { - /** - * Instantiates a new Abstract create control action. - * - * @param nodeTree the node tree - * @param node the node - */ public AbstractCreateControlAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @FXThread @Override + @FXThread protected void process() { super.process(); @@ -49,6 +43,6 @@ protected void process() { * @param parent the parent * @return the control */ - @NotNull - protected abstract Control createControl(@NotNull final Spatial parent); + @FXThread + protected abstract @NotNull Control createControl(@NotNull final Spatial parent); } \ No newline at end of file diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/control/CreateCustomControlAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/control/CreateCustomControlAction.java index 23fdb845..1cb682e4 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/control/CreateCustomControlAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/control/CreateCustomControlAction.java @@ -32,12 +32,14 @@ public CreateCustomControlAction(@NotNull final NodeTree nodeTree, @NotNull f super(nodeTree, node); } + @FXThread @Nullable @Override protected Image getIcon() { return Icons.GEAR_16; } + @FXThread @NotNull @Override protected String getName() { diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/control/CreateMotionControlAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/control/CreateMotionControlAction.java index 2304d177..a8b2a8ba 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/control/CreateMotionControlAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/control/CreateMotionControlAction.java @@ -7,6 +7,7 @@ import com.jme3.scene.Spatial; import com.jme3.scene.control.Control; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; @@ -21,31 +22,25 @@ */ public class CreateMotionControlAction extends AbstractCreateControlAction { - /** - * Instantiates a new Create motion control action. - * - * @param nodeTree the node tree - * @param node the node - */ public CreateMotionControlAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.GEAR_16; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_ADD_CONTROL_NOTION; } - @NotNull @Override - protected Control createControl(@NotNull final Spatial parent) { + @FXThread + protected @NotNull Control createControl(@NotNull final Spatial parent) { final MotionPath motionPath = new MotionPath(); motionPath.addWayPoint(Vector3f.ZERO.clone()); diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/control/physics/CreateCharacterControlAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/control/physics/CreateCharacterControlAction.java index 571256dd..3b2248f2 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/control/physics/CreateCharacterControlAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/control/physics/CreateCharacterControlAction.java @@ -5,6 +5,7 @@ import com.jme3.scene.Spatial; import com.jme3.scene.control.Control; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.model.tree.action.control.AbstractCreateControlAction; import com.ss.editor.ui.control.tree.NodeTree; @@ -20,31 +21,26 @@ */ public class CreateCharacterControlAction extends AbstractCreateControlAction { - /** - * Instantiates a new Create character control action. - * - * @param nodeTree the node tree - * @param node the node - */ public CreateCharacterControlAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.CHARACTER_16; } - @NotNull + @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_ADD_CONTROL_CHARACTER; } - @NotNull @Override - protected Control createControl(@NotNull final Spatial parent) { + @FXThread + protected @NotNull Control createControl(@NotNull final Spatial parent) { return new CharacterControl(new CapsuleCollisionShape(0.6f, 1.8f), 0.03f); } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/control/physics/CreateKinematicRagdollControlAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/control/physics/CreateKinematicRagdollControlAction.java index 6cf15254..75e01604 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/control/physics/CreateKinematicRagdollControlAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/control/physics/CreateKinematicRagdollControlAction.java @@ -4,6 +4,7 @@ import com.jme3.scene.Spatial; import com.jme3.scene.control.Control; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.model.tree.action.control.AbstractCreateControlAction; import com.ss.editor.ui.control.tree.NodeTree; @@ -19,32 +20,25 @@ */ public class CreateKinematicRagdollControlAction extends AbstractCreateControlAction { - /** - * Instantiates a new Create kinematic ragdoll control action. - * - * @param nodeTree the node tree - * @param node the node - */ - public CreateKinematicRagdollControlAction(@NotNull final NodeTree nodeTree, - @NotNull final TreeNode node) { + public CreateKinematicRagdollControlAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable + @FXThread @Override - protected Image getIcon() { + protected @Nullable Image getIcon() { return Icons.ATOM_16; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_ADD_CONTROL_KINEMATIC_RAGDOLL; } - @NotNull @Override - protected Control createControl(@NotNull final Spatial parent) { + @FXThread + protected @NotNull Control createControl(@NotNull final Spatial parent) { return new KinematicRagdollControl(0.5F); } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/control/physics/CreateRigidBodyControlAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/control/physics/CreateRigidBodyControlAction.java index af3c8586..0356eab6 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/control/physics/CreateRigidBodyControlAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/control/physics/CreateRigidBodyControlAction.java @@ -3,6 +3,7 @@ import com.jme3.bullet.control.RigidBodyControl; import com.jme3.scene.Spatial; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.model.tree.action.control.AbstractCreateControlAction; import com.ss.editor.ui.control.tree.NodeTree; @@ -18,31 +19,25 @@ */ public class CreateRigidBodyControlAction extends AbstractCreateControlAction { - /** - * Instantiates a new Create rigid body control action. - * - * @param nodeTree the node tree - * @param node the node - */ public CreateRigidBodyControlAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.RIGID_BODY_16; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_ADD_CONTROL_RIGID_BODY; } - @NotNull @Override - protected RigidBodyControl createControl(@NotNull final Spatial parent) { + @FXThread + protected @NotNull RigidBodyControl createControl(@NotNull final Spatial parent) { final RigidBodyControl rigidBodyControl = new RigidBodyControl(); rigidBodyControl.setEnabled(false); return rigidBodyControl; diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/control/physics/CreateStaticRigidBodyControlAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/control/physics/CreateStaticRigidBodyControlAction.java index 07b53ed6..215ff33f 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/control/physics/CreateStaticRigidBodyControlAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/control/physics/CreateStaticRigidBodyControlAction.java @@ -3,6 +3,7 @@ import com.jme3.bullet.control.RigidBodyControl; import com.jme3.scene.Spatial; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; @@ -17,32 +18,27 @@ */ public class CreateStaticRigidBodyControlAction extends CreateRigidBodyControlAction { - /** - * Instantiates a new Create static rigid body control action. - * - * @param nodeTree the node tree - * @param node the node - */ public CreateStaticRigidBodyControlAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.STATIC_RIGID_BODY_16; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_ADD_CONTROL_STATIC_RIGID_BODY; } - @NotNull + @Override - protected RigidBodyControl createControl(@NotNull final Spatial parent) { + @FXThread + protected @NotNull RigidBodyControl createControl(@NotNull final Spatial parent) { final RigidBodyControl rigidBodyControl = super.createControl(parent); rigidBodyControl.setMass(0F); return rigidBodyControl; diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/control/physics/ReactivatePhysicsControl.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/control/physics/ReactivatePhysicsControl.java index fd397193..2f4b6326 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/control/physics/ReactivatePhysicsControl.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/control/physics/ReactivatePhysicsControl.java @@ -29,12 +29,14 @@ public ReactivatePhysicsControl(@NotNull final NodeTree nodeTree, @NotNull fi super(nodeTree, node); } + @FXThread @Nullable @Override protected Image getIcon() { return Icons.REPLAY_16; } + @FXThread @NotNull @Override protected String getName() { diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/control/physics/vehicle/CreateVehicleControlAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/control/physics/vehicle/CreateVehicleControlAction.java index 71d00792..fed714f9 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/control/physics/vehicle/CreateVehicleControlAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/control/physics/vehicle/CreateVehicleControlAction.java @@ -6,6 +6,7 @@ import com.jme3.scene.Spatial; import com.jme3.scene.control.Control; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.model.tree.action.control.AbstractCreateControlAction; import com.ss.editor.ui.control.tree.NodeTree; @@ -21,31 +22,25 @@ */ public class CreateVehicleControlAction extends AbstractCreateControlAction { - /** - * Instantiates a new Create vehicle control action. - * - * @param nodeTree the node tree - * @param node the node - */ public CreateVehicleControlAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.VEHICLE_16; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_ADD_CONTROL_VEHICLE; } - @NotNull @Override - protected Control createControl(@NotNull final Spatial parent) { + @FXThread + protected @NotNull Control createControl(@NotNull final Spatial parent) { return new VehicleControl(new BoxCollisionShape(new Vector3f(1F, 1F, 1F)), 1F); } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/control/physics/vehicle/CreateVehicleWheelAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/control/physics/vehicle/CreateVehicleWheelAction.java index be5cf823..e0fb9ad1 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/control/physics/vehicle/CreateVehicleWheelAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/control/physics/vehicle/CreateVehicleWheelAction.java @@ -70,12 +70,14 @@ public CreateVehicleWheelAction(@NotNull final NodeTree nodeTree, @NotNull fi super(nodeTree, node); } + @FXThread @NotNull @Override protected String getName() { return Messages.MODEL_NODE_TREE_ACTION_ADD_WHEEL; } + @FXThread @Nullable @Override protected Image getIcon() { diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/control/physics/vehicle/RemoveVehicleWheelAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/control/physics/vehicle/RemoveVehicleWheelAction.java index c503db00..c53596fb 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/control/physics/vehicle/RemoveVehicleWheelAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/control/physics/vehicle/RemoveVehicleWheelAction.java @@ -33,12 +33,14 @@ public RemoveVehicleWheelAction(@NotNull final NodeTree nodeTree, @NotNull fi super(nodeTree, node); } + @FXThread @NotNull @Override protected String getName() { return Messages.MODEL_NODE_TREE_ACTION_REMOVE; } + @FXThread @Nullable @Override protected Image getIcon() { diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/geometry/AbstractCreateGeometryAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/geometry/AbstractCreateGeometryAction.java index 33340b3a..512f7a6d 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/geometry/AbstractCreateGeometryAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/geometry/AbstractCreateGeometryAction.java @@ -22,18 +22,12 @@ */ public abstract class AbstractCreateGeometryAction extends AbstractNodeAction { - /** - * Instantiates a new Abstract create geometry action. - * - * @param nodeTree the node tree - * @param node the node - */ public AbstractCreateGeometryAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @FXThread @Override + @FXThread protected void process() { super.process(); @@ -61,6 +55,6 @@ protected void process() { * * @return the geometry */ - @NotNull - protected abstract Geometry createGeometry(); + @FXThread + protected abstract @NotNull Geometry createGeometry(); } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/geometry/CreateBoxAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/geometry/CreateBoxAction.java index 4e2d62fc..ed670201 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/geometry/CreateBoxAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/geometry/CreateBoxAction.java @@ -3,6 +3,7 @@ import com.jme3.scene.Geometry; import com.jme3.scene.shape.Box; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; @@ -19,31 +20,25 @@ */ public class CreateBoxAction extends AbstractCreateGeometryAction { - /** - * Instantiates a new Create box action. - * - * @param nodeTree the node tree - * @param node the node - */ public CreateBoxAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.CUBE_16; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_CREATE_PRIMITIVE_BOX; } - @NotNull @Override - protected Geometry createGeometry() { + @FXThread + protected @NotNull Geometry createGeometry() { return new Geometry("Box", new Box(1, 1, 1)); } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/geometry/CreateQuadAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/geometry/CreateQuadAction.java index beb4d56a..4c55ccc2 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/geometry/CreateQuadAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/geometry/CreateQuadAction.java @@ -3,6 +3,7 @@ import com.jme3.scene.Geometry; import com.jme3.scene.shape.Quad; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; @@ -19,31 +20,25 @@ */ public class CreateQuadAction extends AbstractCreateGeometryAction { - /** - * Instantiates a new Create quad action. - * - * @param nodeTree the node tree - * @param node the node - */ public CreateQuadAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.PLANE_16; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_CREATE_PRIMITIVE_QUAD; } - @NotNull @Override - protected Geometry createGeometry() { + @FXThread + protected @NotNull Geometry createGeometry() { return new Geometry("Quad", new Quad(2, 2)); } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/geometry/CreateSphereAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/geometry/CreateSphereAction.java index 6842fb44..04ce67a3 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/geometry/CreateSphereAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/geometry/CreateSphereAction.java @@ -3,6 +3,7 @@ import com.jme3.scene.Geometry; import com.jme3.scene.shape.Sphere; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; @@ -19,31 +20,25 @@ */ public class CreateSphereAction extends AbstractCreateGeometryAction { - /** - * Instantiates a new Create sphere action. - * - * @param nodeTree the node tree - * @param node the node - */ public CreateSphereAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.SPHERE_16; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_CREATE_PRIMITIVE_SPHERE; } - @NotNull @Override - protected Geometry createGeometry() { + @FXThread + protected @NotNull Geometry createGeometry() { return new Geometry("Sphere", new Sphere(30, 30, 1)); } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/geometry/GenerateLoDAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/geometry/GenerateLoDAction.java index 1487d45d..11449c17 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/geometry/GenerateLoDAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/geometry/GenerateLoDAction.java @@ -22,30 +22,24 @@ */ public class GenerateLoDAction extends AbstractNodeAction { - /** - * Instantiates a new Generate lo d action. - * - * @param nodeTree the node tree - * @param node the node - */ public GenerateLoDAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.MESH_16; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_LOD_GENERATOR; } - @FXThread @Override + @FXThread protected void process() { final GeometryTreeNode modelNode = ClassUtils.unsafeCast(getNode()); final Geometry geometry = modelNode.getElement(); diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/light/AbstractCreateLightAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/light/AbstractCreateLightAction.java index ffc0d4e8..1aaafea9 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/light/AbstractCreateLightAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/light/AbstractCreateLightAction.java @@ -20,18 +20,12 @@ */ public abstract class AbstractCreateLightAction extends AbstractNodeAction { - /** - * Instantiates a new Abstract create light action. - * - * @param nodeTree the node tree - * @param node the node - */ public AbstractCreateLightAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @FXThread @Override + @FXThread protected void process() { super.process(); @@ -50,6 +44,6 @@ protected void process() { * * @return the light */ - @NotNull - protected abstract Light createLight(); + @FXThread + protected abstract @NotNull Light createLight(); } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/light/CreateAmbientLightAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/light/CreateAmbientLightAction.java index d9eecd10..9b9983f4 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/light/CreateAmbientLightAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/light/CreateAmbientLightAction.java @@ -3,6 +3,7 @@ import com.jme3.light.AmbientLight; import com.jme3.light.Light; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; @@ -19,31 +20,25 @@ */ public class CreateAmbientLightAction extends AbstractCreateLightAction { - /** - * Instantiates a new Create ambient light action. - * - * @param nodeTree the node tree - * @param node the node - */ public CreateAmbientLightAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.AMBIENT_16; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_AMBIENT_LIGHT; } - @NotNull @Override - protected Light createLight() { + @FXThread + protected @NotNull Light createLight() { return new AmbientLight(); } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/light/CreateDirectionLightAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/light/CreateDirectionLightAction.java index 69d34ea0..ffc9453b 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/light/CreateDirectionLightAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/light/CreateDirectionLightAction.java @@ -3,6 +3,7 @@ import com.jme3.light.DirectionalLight; import com.jme3.light.Light; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; @@ -19,31 +20,25 @@ */ public class CreateDirectionLightAction extends AbstractCreateLightAction { - /** - * Instantiates a new Create direction light action. - * - * @param nodeTree the node tree - * @param node the node - */ public CreateDirectionLightAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.SUN_16; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_DIRECTION_LIGHT; } - @NotNull @Override - protected Light createLight() { + @FXThread + protected @NotNull Light createLight() { return new DirectionalLight(); } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/light/CreatePointLightAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/light/CreatePointLightAction.java index 84af5133..ec040b17 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/light/CreatePointLightAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/light/CreatePointLightAction.java @@ -3,6 +3,7 @@ import com.jme3.light.Light; import com.jme3.light.PointLight; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; @@ -19,31 +20,26 @@ */ public class CreatePointLightAction extends AbstractCreateLightAction { - /** - * Instantiates a new Create point light action. - * - * @param nodeTree the node tree - * @param node the node - */ public CreatePointLightAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.POINT_LIGHT_16; } - @NotNull + @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_POINT_LIGHT; } - @NotNull @Override - protected Light createLight() { + @FXThread + protected @NotNull Light createLight() { return new PointLight(); } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/light/CreateSpotLightAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/light/CreateSpotLightAction.java index 14b6ecac..dca192de 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/light/CreateSpotLightAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/light/CreateSpotLightAction.java @@ -3,6 +3,7 @@ import com.jme3.light.Light; import com.jme3.light.SpotLight; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; @@ -19,31 +20,25 @@ */ public class CreateSpotLightAction extends AbstractCreateLightAction { - /** - * Instantiates a new Create spot light action. - * - * @param nodeTree the node tree - * @param node the node - */ public CreateSpotLightAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.LAMP_16; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_SPOT_LIGHT; } - @NotNull @Override - protected Light createLight() { + @FXThread + protected @NotNull Light createLight() { return new SpotLight(); } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/CreateParticleEmitterAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/CreateParticleEmitterAction.java index 15735df8..23c7e8f2 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/CreateParticleEmitterAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/CreateParticleEmitterAction.java @@ -34,12 +34,14 @@ public CreateParticleEmitterAction(@NotNull final NodeTree nodeTree, @NotNull super(nodeTree, node); } + @FXThread @Nullable @Override protected Image getIcon() { return Icons.EMITTER_16; } + @FXThread @NotNull @Override protected String getName() { diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/ResetParticleEmittersAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/ResetParticleEmittersAction.java index 34a483a1..b6b4f5d2 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/ResetParticleEmittersAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/ResetParticleEmittersAction.java @@ -32,12 +32,14 @@ public ResetParticleEmittersAction(@NotNull final NodeTree nodeTree, @NotNull super(nodeTree, node); } + @FXThread @Nullable @Override protected Image getIcon() { return Icons.REPLAY_16; } + @FXThread @NotNull @Override protected String getName() { diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/influencer/AbstractCreateParticleInfluencerAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/influencer/AbstractCreateParticleInfluencerAction.java index 87c3aca4..7bd4bb33 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/influencer/AbstractCreateParticleInfluencerAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/influencer/AbstractCreateParticleInfluencerAction.java @@ -6,9 +6,9 @@ import com.ss.editor.annotation.FXThread; import com.ss.editor.model.undo.editor.ModelChangeConsumer; import com.ss.editor.ui.Icons; -import com.ss.editor.ui.control.tree.action.AbstractNodeAction; import com.ss.editor.ui.control.model.tree.action.operation.particle.emitter.ChangeParticleInfluencerOperation; import com.ss.editor.ui.control.tree.NodeTree; +import com.ss.editor.ui.control.tree.action.AbstractNodeAction; import com.ss.editor.ui.control.tree.node.TreeNode; import javafx.scene.image.Image; import org.jetbrains.annotations.NotNull; @@ -21,25 +21,19 @@ */ public abstract class AbstractCreateParticleInfluencerAction extends AbstractNodeAction { - /** - * Instantiates a new Abstract create particle influencer action. - * - * @param nodeTree the node tree - * @param node the node - */ public AbstractCreateParticleInfluencerAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.INFLUENCER_16; } - @FXThread @Override + @FXThread protected void process() { super.process(); @@ -58,6 +52,6 @@ protected void process() { * * @return the particle influencer */ - @NotNull - protected abstract ParticleInfluencer createInfluencer(); + @FXThread + protected abstract @NotNull ParticleInfluencer createInfluencer(); } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/influencer/CreateDefaultParticleInfluencerAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/influencer/CreateDefaultParticleInfluencerAction.java index 699c1250..99a7070a 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/influencer/CreateDefaultParticleInfluencerAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/influencer/CreateDefaultParticleInfluencerAction.java @@ -3,6 +3,7 @@ import com.jme3.effect.influencers.DefaultParticleInfluencer; import com.jme3.effect.influencers.ParticleInfluencer; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; import org.jetbrains.annotations.NotNull; @@ -14,26 +15,19 @@ */ public class CreateDefaultParticleInfluencerAction extends AbstractCreateParticleInfluencerAction { - /** - * Instantiates a new Create default particle influencer action. - * - * @param nodeTree the node tree - * @param node the node - */ - public CreateDefaultParticleInfluencerAction(@NotNull final NodeTree nodeTree, - @NotNull final TreeNode node) { + public CreateDefaultParticleInfluencerAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @NotNull @Override - protected ParticleInfluencer createInfluencer() { + @FXThread + protected @NotNull ParticleInfluencer createInfluencer() { return new DefaultParticleInfluencer(); } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_INFLUENCER_DEFAULT; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/influencer/CreateEmptyParticleInfluencerAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/influencer/CreateEmptyParticleInfluencerAction.java index c4cedd79..59973336 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/influencer/CreateEmptyParticleInfluencerAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/influencer/CreateEmptyParticleInfluencerAction.java @@ -3,6 +3,7 @@ import com.jme3.effect.influencers.EmptyParticleInfluencer; import com.jme3.effect.influencers.ParticleInfluencer; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; import org.jetbrains.annotations.NotNull; @@ -14,26 +15,19 @@ */ public class CreateEmptyParticleInfluencerAction extends AbstractCreateParticleInfluencerAction { - /** - * Instantiates a new Create empty particle influencer action. - * - * @param nodeTree the node tree - * @param node the node - */ - public CreateEmptyParticleInfluencerAction(@NotNull final NodeTree nodeTree, - @NotNull final TreeNode node) { + public CreateEmptyParticleInfluencerAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @NotNull @Override - protected ParticleInfluencer createInfluencer() { + @FXThread + protected @NotNull ParticleInfluencer createInfluencer() { return new EmptyParticleInfluencer(); } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_INFLUENCER_EMPTY; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/influencer/CreateRadialParticleInfluencerAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/influencer/CreateRadialParticleInfluencerAction.java index 3984b7ff..8070c157 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/influencer/CreateRadialParticleInfluencerAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/influencer/CreateRadialParticleInfluencerAction.java @@ -4,6 +4,7 @@ import com.jme3.effect.influencers.ParticleInfluencer; import com.jme3.effect.influencers.RadialParticleInfluencer; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; import org.jetbrains.annotations.NotNull; @@ -15,26 +16,19 @@ */ public class CreateRadialParticleInfluencerAction extends AbstractCreateParticleInfluencerAction { - /** - * Instantiates a new Create radial particle influencer action. - * - * @param nodeTree the node tree - * @param node the node - */ - public CreateRadialParticleInfluencerAction(@NotNull final NodeTree nodeTree, - @NotNull final TreeNode node) { + public CreateRadialParticleInfluencerAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @NotNull @Override - protected ParticleInfluencer createInfluencer() { + @FXThread + protected @NotNull ParticleInfluencer createInfluencer() { return new RadialParticleInfluencer(); } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_INFLUENCER_RADIAL; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/shape/AbstractCreateShapeEmitterAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/shape/AbstractCreateShapeEmitterAction.java index 26bb10d2..2f1f0c90 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/shape/AbstractCreateShapeEmitterAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/shape/AbstractCreateShapeEmitterAction.java @@ -9,9 +9,9 @@ import com.ss.editor.plugin.api.dialog.GenericFactoryDialog; import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.editor.ui.Icons; -import com.ss.editor.ui.control.tree.action.AbstractNodeAction; import com.ss.editor.ui.control.model.tree.action.operation.particle.emitter.ChangeEmitterShapeOperation; import com.ss.editor.ui.control.tree.NodeTree; +import com.ss.editor.ui.control.tree.action.AbstractNodeAction; import com.ss.editor.ui.control.tree.node.TreeNode; import com.ss.rlib.util.VarTable; import com.ss.rlib.util.array.Array; @@ -28,20 +28,13 @@ */ public abstract class AbstractCreateShapeEmitterAction extends AbstractNodeAction { - /** - * Instantiates a new Abstract create shape emitter action. - * - * @param nodeTree the node tree - * @param node the node - */ - public AbstractCreateShapeEmitterAction(@NotNull final NodeTree nodeTree, - @NotNull final TreeNode node) { + public AbstractCreateShapeEmitterAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.GEOMETRY_16; } @@ -67,8 +60,8 @@ protected void process() { * * @return the dialog size or null. */ - @Nullable - protected Point getDialogSize() { + @FXThread + protected @Nullable Point getDialogSize() { return null; } @@ -77,14 +70,15 @@ protected Point getDialogSize() { * * @return the dialog title. */ - @NotNull - protected abstract String getDialogTitle(); + @FXThread + protected abstract @NotNull String getDialogTitle(); /** * Handle the result from the dialog. * * @param vars the table with variables. */ + @FXThread private void handleResult(@NotNull final VarTable vars) { final TreeNode treeNode = getNode(); @@ -101,8 +95,8 @@ private void handleResult(@NotNull final VarTable vars) { * * @return the list of definitions. */ - @NotNull - protected abstract Array getPropertyDefinitions(); + @FXThread + protected abstract @NotNull Array getPropertyDefinitions(); /** * Create emitter shape. @@ -110,6 +104,6 @@ private void handleResult(@NotNull final VarTable vars) { * @param vars the table with variables. * @return the emitter shape. */ - @NotNull - protected abstract EmitterShape createEmitterShape(@NotNull final VarTable vars); + @FXThread + protected abstract @NotNull EmitterShape createEmitterShape(@NotNull final VarTable vars); } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/shape/CreateBoxShapeEmitterAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/shape/CreateBoxShapeEmitterAction.java index e2c67287..12911038 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/shape/CreateBoxShapeEmitterAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/shape/CreateBoxShapeEmitterAction.java @@ -6,6 +6,7 @@ import com.jme3.effect.shapes.EmitterShape; import com.jme3.math.Vector3f; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; @@ -30,46 +31,40 @@ public class CreateBoxShapeEmitterAction extends AbstractCreateShapeEmitterActio @NotNull private static final String PROPERTY_MAX = "max"; - /** - * Instantiates a new Create box shape emitter action. - * - * @param nodeTree the node tree - * @param node the node - */ public CreateBoxShapeEmitterAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.CUBE_16; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_BOX_SHAPE; } - @NotNull @Override - protected Array getPropertyDefinitions() { + @FXThread + protected @NotNull Array getPropertyDefinitions() { final Array definitions = ArrayFactory.newArray(PropertyDefinition.class); definitions.add(new PropertyDefinition(VECTOR_3F, Messages.MODEL_PROPERTY_MIN, PROPERTY_MIN, new Vector3f(1F, 1F, 1F))); definitions.add(new PropertyDefinition(VECTOR_3F, Messages.MODEL_PROPERTY_MAX, PROPERTY_MAX, new Vector3f(1F, 1F, 1F))); return definitions; } - @NotNull @Override - protected String getDialogTitle() { + @FXThread + protected @NotNull String getDialogTitle() { return Messages.CREATE_PARTICLE_EMITTER_BOX_SHAPE_DIALOG_TITLE; } - @NotNull @Override - protected EmitterShape createEmitterShape(@NotNull final VarTable vars) { + @FXThread + protected @NotNull EmitterShape createEmitterShape(@NotNull final VarTable vars) { final Vector3f min = vars.get(PROPERTY_MIN); final Vector3f max = vars.get(PROPERTY_MAX); return new EmitterBoxShape(min, max); diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/shape/CreateMeshConvexHullShapeEmitterAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/shape/CreateMeshConvexHullShapeEmitterAction.java index 21fbe44d..72f509e9 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/shape/CreateMeshConvexHullShapeEmitterAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/shape/CreateMeshConvexHullShapeEmitterAction.java @@ -5,6 +5,7 @@ import com.jme3.effect.shapes.EmitterMeshVertexShape; import com.jme3.scene.Mesh; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; import org.jetbrains.annotations.NotNull; @@ -18,26 +19,20 @@ */ public class CreateMeshConvexHullShapeEmitterAction extends CreateMeshVertexShapeEmitterAction { - /** - * Instantiates a new Create mesh convex hull shape emitter action. - * - * @param nodeTree the node tree - * @param node the node - */ public CreateMeshConvexHullShapeEmitterAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @NotNull + @FXThread @Override - protected EmitterMeshVertexShape createEmitterShape(@NotNull final List meshes) { + protected @NotNull EmitterMeshVertexShape createEmitterShape(@NotNull final List meshes) { return new EmitterMeshConvexHullShape(meshes); } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_MESH_CONVEX_HULL_SHAPE; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/shape/CreateMeshFaceShapeEmitterAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/shape/CreateMeshFaceShapeEmitterAction.java index e00567d4..0056dff9 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/shape/CreateMeshFaceShapeEmitterAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/shape/CreateMeshFaceShapeEmitterAction.java @@ -5,6 +5,7 @@ import com.jme3.effect.shapes.EmitterMeshVertexShape; import com.jme3.scene.Mesh; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; import org.jetbrains.annotations.NotNull; @@ -18,26 +19,19 @@ */ public class CreateMeshFaceShapeEmitterAction extends CreateMeshVertexShapeEmitterAction { - /** - * Instantiates a new Create mesh face shape emitter action. - * - * @param nodeTree the node tree - * @param node the node - */ - public CreateMeshFaceShapeEmitterAction(@NotNull final NodeTree nodeTree, - @NotNull final TreeNode node) { + public CreateMeshFaceShapeEmitterAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @NotNull @Override - protected EmitterMeshVertexShape createEmitterShape(@NotNull final List meshes) { + @FXThread + protected @NotNull EmitterMeshVertexShape createEmitterShape(@NotNull final List meshes) { return new EmitterMeshFaceShape(meshes); } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_MESH_FACE_SHAPE; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/shape/CreateMeshVertexShapeEmitterAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/shape/CreateMeshVertexShapeEmitterAction.java index b441a761..3fa0969b 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/shape/CreateMeshVertexShapeEmitterAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/shape/CreateMeshVertexShapeEmitterAction.java @@ -8,10 +8,11 @@ import com.jme3.scene.Geometry; import com.jme3.scene.Mesh; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; -import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.rlib.util.VarTable; import com.ss.rlib.util.array.Array; import com.ss.rlib.util.array.ArrayFactory; @@ -31,53 +32,46 @@ public class CreateMeshVertexShapeEmitterAction extends AbstractCreateShapeEmitt @NotNull private static final String PROPERTY_GEOMETRY = "geometry"; - /** - * Instantiates a new Create mesh vertex shape emitter action. - * - * @param nodeTree the node tree - * @param node the node - */ - public CreateMeshVertexShapeEmitterAction(@NotNull final NodeTree nodeTree, - @NotNull final TreeNode node) { + public CreateMeshVertexShapeEmitterAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.MESH_16; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_MESH_VERTEX_SHAPE; } - @NotNull @Override - protected Array getPropertyDefinitions() { + @FXThread + protected @NotNull Array getPropertyDefinitions() { final Array definitions = ArrayFactory.newArray(PropertyDefinition.class); definitions.add(new PropertyDefinition(GEOMETRY_FROM_ASSET_FOLDER, Messages.MODEL_PROPERTY_GEOMETRY, PROPERTY_GEOMETRY, null)); return definitions; } - @NotNull @Override - protected String getDialogTitle() { + @FXThread + protected @NotNull String getDialogTitle() { return Messages.ASSET_EDITOR_DIALOG_TITLE; } - @NotNull @Override - protected EmitterShape createEmitterShape(@NotNull final VarTable vars) { + @FXThread + protected @NotNull EmitterShape createEmitterShape(@NotNull final VarTable vars) { final Geometry geometry = vars.get(PROPERTY_GEOMETRY); final List meshes = singletonList(geometry.getMesh()); return createEmitterShape(meshes); } - @NotNull - protected EmitterMeshVertexShape createEmitterShape(final List meshes) { + @FXThread + protected @NotNull EmitterMeshVertexShape createEmitterShape(final List meshes) { return new EmitterMeshVertexShape(meshes); } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/shape/CreatePointShapeEmitterAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/shape/CreatePointShapeEmitterAction.java index 6d194d7b..419bc3aa 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/shape/CreatePointShapeEmitterAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/shape/CreatePointShapeEmitterAction.java @@ -6,10 +6,11 @@ import com.jme3.effect.shapes.EmitterShape; import com.jme3.math.Vector3f; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; -import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.rlib.util.VarTable; import com.ss.rlib.util.array.Array; import com.ss.rlib.util.array.ArrayFactory; @@ -27,46 +28,39 @@ public class CreatePointShapeEmitterAction extends AbstractCreateShapeEmitterAct @NotNull private static final String PROPERTY_POINT = "point"; - /** - * Instantiates a new Create point shape emitter action. - * - * @param nodeTree the node tree - * @param node the node - */ - public CreatePointShapeEmitterAction(@NotNull final NodeTree nodeTree, - @NotNull final TreeNode node) { + public CreatePointShapeEmitterAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.POINTS_16; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_POINT_SHAPE; } - @NotNull @Override - protected Array getPropertyDefinitions() { + @FXThread + protected @NotNull Array getPropertyDefinitions() { final Array definitions = ArrayFactory.newArray(PropertyDefinition.class); definitions.add(new PropertyDefinition(VECTOR_3F, Messages.MODEL_PROPERTY_POINT, PROPERTY_POINT, new Vector3f(1F, 1F, 1F))); return definitions; } - @NotNull @Override - protected String getDialogTitle() { + @FXThread + protected @NotNull String getDialogTitle() { return Messages.CREATE_PARTICLE_EMITTER_POINT_SHAPE_DIALOG_TITLE; } - @NotNull @Override - protected EmitterShape createEmitterShape(@NotNull final VarTable vars) { + @FXThread + protected @NotNull EmitterShape createEmitterShape(@NotNull final VarTable vars) { final Vector3f point = vars.get(PROPERTY_POINT); return new EmitterPointShape(point); } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/shape/CreateSphereShapeEmitterAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/shape/CreateSphereShapeEmitterAction.java index a9e670be..baf07ad2 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/shape/CreateSphereShapeEmitterAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/shape/CreateSphereShapeEmitterAction.java @@ -7,10 +7,11 @@ import com.jme3.effect.shapes.EmitterSphereShape; import com.jme3.math.Vector3f; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; -import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.rlib.util.VarTable; import com.ss.rlib.util.array.Array; import com.ss.rlib.util.array.ArrayFactory; @@ -31,47 +32,40 @@ public class CreateSphereShapeEmitterAction extends AbstractCreateShapeEmitterAc @NotNull private static final String PROPERTY_RADIUS = "radius"; - /** - * Instantiates a new Create sphere shape emitter action. - * - * @param nodeTree the node tree - * @param node the node - */ - public CreateSphereShapeEmitterAction(@NotNull final NodeTree nodeTree, - @NotNull final TreeNode node) { + public CreateSphereShapeEmitterAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.SPHERE_16; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_SPHERE_SHAPE; } - @NotNull @Override - protected Array getPropertyDefinitions() { + @FXThread + protected @NotNull Array getPropertyDefinitions() { final Array definitions = ArrayFactory.newArray(PropertyDefinition.class); definitions.add(new PropertyDefinition(VECTOR_3F, Messages.MODEL_PROPERTY_CENTER, PROPERTY_CENTER, new Vector3f(1F, 1F, 1F))); definitions.add(new PropertyDefinition(FLOAT, Messages.MODEL_PROPERTY_RADIUS, PROPERTY_RADIUS, 1F)); return definitions; } - @NotNull @Override - protected String getDialogTitle() { + @FXThread + protected @NotNull String getDialogTitle() { return Messages.CREATE_PARTICLE_EMITTER_SPHERE_SHAPE_DIALOG_TITLE; } - @NotNull @Override - protected EmitterShape createEmitterShape(@NotNull final VarTable vars) { + @FXThread + protected @NotNull EmitterShape createEmitterShape(@NotNull final VarTable vars) { final Vector3f center = vars.get(PROPERTY_CENTER); final float radius = vars.getFloat(PROPERTY_RADIUS); return new EmitterSphereShape(center, radius); diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/CreateToneg0dParticleEmitterAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/CreateToneg0dParticleEmitterAction.java index d4901e45..efb7b007 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/CreateToneg0dParticleEmitterAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/CreateToneg0dParticleEmitterAction.java @@ -38,12 +38,14 @@ public CreateToneg0dParticleEmitterAction(@NotNull final NodeTree nodeTree, @ super(nodeTree, node); } + @FXThread @Nullable @Override protected Image getIcon() { return Icons.EMITTER_16; } + @FXThread @NotNull @Override protected String getName() { diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/CreateToneg0dSoftParticleEmitterAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/CreateToneg0dSoftParticleEmitterAction.java index 52922404..3adf2ba2 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/CreateToneg0dSoftParticleEmitterAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/CreateToneg0dSoftParticleEmitterAction.java @@ -1,6 +1,7 @@ package com.ss.editor.ui.control.model.tree.action.particle.emitter.toneg0d; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; @@ -26,6 +27,7 @@ public CreateToneg0dSoftParticleEmitterAction(@NotNull final NodeTree nodeTre super(nodeTree, node); } + @FXThread @NotNull @Override protected String getName() { diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/AbstractCreateParticleInfluencerAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/AbstractCreateParticleInfluencerAction.java index a226d917..a5f5c1c9 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/AbstractCreateParticleInfluencerAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/AbstractCreateParticleInfluencerAction.java @@ -5,9 +5,9 @@ import com.ss.editor.model.node.particles.Toneg0dParticleInfluencers; import com.ss.editor.model.undo.editor.ModelChangeConsumer; import com.ss.editor.ui.Icons; -import com.ss.editor.ui.control.tree.action.AbstractNodeAction; import com.ss.editor.ui.control.model.tree.action.operation.particle.emitter.toneg0d.AddParticleInfluencerOperation; import com.ss.editor.ui.control.tree.NodeTree; +import com.ss.editor.ui.control.tree.action.AbstractNodeAction; import com.ss.editor.ui.control.tree.node.TreeNode; import javafx.scene.image.Image; import org.jetbrains.annotations.NotNull; @@ -22,24 +22,19 @@ */ public abstract class AbstractCreateParticleInfluencerAction extends AbstractNodeAction { - /** - * Instantiates a new Abstract create particle influencer action. - * - * @param nodeTree the node tree - * @param node the node - */ - public AbstractCreateParticleInfluencerAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { + public AbstractCreateParticleInfluencerAction(@NotNull final NodeTree nodeTree, + @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.INFLUENCER_16; } - @FXThread @Override + @FXThread protected void process() { super.process(); @@ -59,6 +54,6 @@ protected void process() { * * @return the particle influencer */ - @NotNull - protected abstract ParticleInfluencer createInfluencer(); + @FXThread + protected abstract @NotNull ParticleInfluencer createInfluencer(); } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreateAlphaParticleInfluencerAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreateAlphaParticleInfluencerAction.java index 43ec16b7..df8d6950 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreateAlphaParticleInfluencerAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreateAlphaParticleInfluencerAction.java @@ -1,11 +1,10 @@ package com.ss.editor.ui.control.model.tree.action.particle.emitter.toneg0d.influerencer; +import com.ss.editor.annotation.FXThread; import com.ss.editor.model.undo.editor.ModelChangeConsumer; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; - import org.jetbrains.annotations.NotNull; - import tonegod.emitter.Messages; import tonegod.emitter.ParticleEmitterNode; import tonegod.emitter.influencers.ParticleInfluencer; @@ -18,25 +17,20 @@ */ public class CreateAlphaParticleInfluencerAction extends AbstractCreateParticleInfluencerAction { - /** - * Instantiates a new Create alpha particle influencer action. - * - * @param nodeTree the node tree - * @param node the node - */ - public CreateAlphaParticleInfluencerAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { + public CreateAlphaParticleInfluencerAction(@NotNull final NodeTree nodeTree, + @NotNull final TreeNode node) { super(nodeTree, node); } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.PARTICLE_INFLUENCER_ALPHA; } - @NotNull @Override - protected ParticleInfluencer createInfluencer() { + @FXThread + protected @NotNull ParticleInfluencer createInfluencer() { return new AlphaInfluencer(); } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreateColorParticleInfluencerAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreateColorParticleInfluencerAction.java index 8a5d6217..bfc2984b 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreateColorParticleInfluencerAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreateColorParticleInfluencerAction.java @@ -1,11 +1,10 @@ package com.ss.editor.ui.control.model.tree.action.particle.emitter.toneg0d.influerencer; +import com.ss.editor.annotation.FXThread; import com.ss.editor.model.undo.editor.ModelChangeConsumer; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; - import org.jetbrains.annotations.NotNull; - import tonegod.emitter.Messages; import tonegod.emitter.ParticleEmitterNode; import tonegod.emitter.influencers.ParticleInfluencer; @@ -18,25 +17,20 @@ */ public class CreateColorParticleInfluencerAction extends AbstractCreateParticleInfluencerAction { - /** - * Instantiates a new Create color particle influencer action. - * - * @param nodeTree the node tree - * @param node the node - */ - public CreateColorParticleInfluencerAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { + public CreateColorParticleInfluencerAction(@NotNull final NodeTree nodeTree, + @NotNull final TreeNode node) { super(nodeTree, node); } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.PARTICLE_INFLUENCER_COLOR; } - @NotNull @Override - protected ParticleInfluencer createInfluencer() { + @FXThread + protected @NotNull ParticleInfluencer createInfluencer() { return new ColorInfluencer(); } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreateDestinationParticleInfluencerAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreateDestinationParticleInfluencerAction.java index 9c38a16c..0a3420d7 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreateDestinationParticleInfluencerAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreateDestinationParticleInfluencerAction.java @@ -1,11 +1,10 @@ package com.ss.editor.ui.control.model.tree.action.particle.emitter.toneg0d.influerencer; +import com.ss.editor.annotation.FXThread; import com.ss.editor.model.undo.editor.ModelChangeConsumer; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; - import org.jetbrains.annotations.NotNull; - import tonegod.emitter.Messages; import tonegod.emitter.ParticleEmitterNode; import tonegod.emitter.influencers.ParticleInfluencer; @@ -18,25 +17,20 @@ */ public class CreateDestinationParticleInfluencerAction extends AbstractCreateParticleInfluencerAction { - /** - * Instantiates a new Create destination particle influencer action. - * - * @param nodeTree the node tree - * @param node the node - */ - public CreateDestinationParticleInfluencerAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { + public CreateDestinationParticleInfluencerAction(@NotNull final NodeTree nodeTree, + @NotNull final TreeNode node) { super(nodeTree, node); } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.PARTICLE_INFLUENCER_DESTINATION; } - @NotNull @Override - protected ParticleInfluencer createInfluencer() { + @FXThread + protected @NotNull ParticleInfluencer createInfluencer() { return new DestinationInfluencer(); } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreateGravityParticleInfluencerAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreateGravityParticleInfluencerAction.java index 4ec999ea..3c5104d6 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreateGravityParticleInfluencerAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreateGravityParticleInfluencerAction.java @@ -1,11 +1,10 @@ package com.ss.editor.ui.control.model.tree.action.particle.emitter.toneg0d.influerencer; +import com.ss.editor.annotation.FXThread; import com.ss.editor.model.undo.editor.ModelChangeConsumer; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; - import org.jetbrains.annotations.NotNull; - import tonegod.emitter.Messages; import tonegod.emitter.ParticleEmitterNode; import tonegod.emitter.influencers.ParticleInfluencer; @@ -18,25 +17,20 @@ */ public class CreateGravityParticleInfluencerAction extends AbstractCreateParticleInfluencerAction { - /** - * Instantiates a new Create gravity particle influencer action. - * - * @param nodeTree the node tree - * @param node the node - */ - public CreateGravityParticleInfluencerAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { + public CreateGravityParticleInfluencerAction(@NotNull final NodeTree nodeTree, + @NotNull final TreeNode node) { super(nodeTree, node); } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.PARTICLE_INFLUENCER_GRAVITY; } - @NotNull @Override - protected ParticleInfluencer createInfluencer() { + @FXThread + protected @NotNull ParticleInfluencer createInfluencer() { return new GravityInfluencer(); } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreateImpulseParticleInfluencerAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreateImpulseParticleInfluencerAction.java index d78cbff5..fadb61b2 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreateImpulseParticleInfluencerAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreateImpulseParticleInfluencerAction.java @@ -1,11 +1,10 @@ package com.ss.editor.ui.control.model.tree.action.particle.emitter.toneg0d.influerencer; +import com.ss.editor.annotation.FXThread; import com.ss.editor.model.undo.editor.ModelChangeConsumer; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; - import org.jetbrains.annotations.NotNull; - import tonegod.emitter.Messages; import tonegod.emitter.ParticleEmitterNode; import tonegod.emitter.influencers.ParticleInfluencer; @@ -18,25 +17,20 @@ */ public class CreateImpulseParticleInfluencerAction extends AbstractCreateParticleInfluencerAction { - /** - * Instantiates a new Create impulse particle influencer action. - * - * @param nodeTree the node tree - * @param node the node - */ - public CreateImpulseParticleInfluencerAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { + public CreateImpulseParticleInfluencerAction(@NotNull final NodeTree nodeTree, + @NotNull final TreeNode node) { super(nodeTree, node); } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.PARTICLE_INFLUENCER_IMPULSE; } - @NotNull @Override - protected ParticleInfluencer createInfluencer() { + @FXThread + protected @NotNull ParticleInfluencer createInfluencer() { return new ImpulseInfluencer(); } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreatePhysicsParticleInfluencerAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreatePhysicsParticleInfluencerAction.java index 6f61c4f3..4f22a9a8 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreatePhysicsParticleInfluencerAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreatePhysicsParticleInfluencerAction.java @@ -1,11 +1,10 @@ package com.ss.editor.ui.control.model.tree.action.particle.emitter.toneg0d.influerencer; +import com.ss.editor.annotation.FXThread; import com.ss.editor.model.undo.editor.ModelChangeConsumer; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; - import org.jetbrains.annotations.NotNull; - import tonegod.emitter.Messages; import tonegod.emitter.ParticleEmitterNode; import tonegod.emitter.influencers.ParticleInfluencer; @@ -18,25 +17,20 @@ */ public class CreatePhysicsParticleInfluencerAction extends AbstractCreateParticleInfluencerAction { - /** - * Instantiates a new Create physics particle influencer action. - * - * @param nodeTree the node tree - * @param node the node - */ - public CreatePhysicsParticleInfluencerAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { + public CreatePhysicsParticleInfluencerAction(@NotNull final NodeTree nodeTree, + @NotNull final TreeNode node) { super(nodeTree, node); } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.PARTICLE_INFLUENCER_PHYSICS; } - @NotNull @Override - protected ParticleInfluencer createInfluencer() { + @FXThread + protected @NotNull ParticleInfluencer createInfluencer() { return new PhysicsInfluencer(); } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreateRadialVelocityParticleInfluencerAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreateRadialVelocityParticleInfluencerAction.java index 69bd69a7..05d95a2e 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreateRadialVelocityParticleInfluencerAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreateRadialVelocityParticleInfluencerAction.java @@ -1,11 +1,10 @@ package com.ss.editor.ui.control.model.tree.action.particle.emitter.toneg0d.influerencer; +import com.ss.editor.annotation.FXThread; import com.ss.editor.model.undo.editor.ModelChangeConsumer; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; - import org.jetbrains.annotations.NotNull; - import tonegod.emitter.Messages; import tonegod.emitter.ParticleEmitterNode; import tonegod.emitter.influencers.ParticleInfluencer; @@ -18,25 +17,20 @@ */ public class CreateRadialVelocityParticleInfluencerAction extends AbstractCreateParticleInfluencerAction { - /** - * Instantiates a new Create radial velocity particle influencer action. - * - * @param nodeTree the node tree - * @param node the node - */ - public CreateRadialVelocityParticleInfluencerAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { + public CreateRadialVelocityParticleInfluencerAction(@NotNull final NodeTree nodeTree, + @NotNull final TreeNode node) { super(nodeTree, node); } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.PARTICLE_INFLUENCER_RADIAL_VELOCITY; } - @NotNull @Override - protected ParticleInfluencer createInfluencer() { + @FXThread + protected @NotNull ParticleInfluencer createInfluencer() { return new RadialVelocityInfluencer(); } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreateRotationParticleInfluencerAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreateRotationParticleInfluencerAction.java index 112a78b6..f2c1ea48 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreateRotationParticleInfluencerAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreateRotationParticleInfluencerAction.java @@ -1,11 +1,10 @@ package com.ss.editor.ui.control.model.tree.action.particle.emitter.toneg0d.influerencer; +import com.ss.editor.annotation.FXThread; import com.ss.editor.model.undo.editor.ModelChangeConsumer; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; - import org.jetbrains.annotations.NotNull; - import tonegod.emitter.Messages; import tonegod.emitter.ParticleEmitterNode; import tonegod.emitter.influencers.ParticleInfluencer; @@ -18,25 +17,20 @@ */ public class CreateRotationParticleInfluencerAction extends AbstractCreateParticleInfluencerAction { - /** - * Instantiates a new Create rotation particle influencer action. - * - * @param nodeTree the node tree - * @param node the node - */ - public CreateRotationParticleInfluencerAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { + public CreateRotationParticleInfluencerAction(@NotNull final NodeTree nodeTree, + @NotNull final TreeNode node) { super(nodeTree, node); } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.PARTICLE_INFLUENCER_ROTATION; } - @NotNull @Override - protected ParticleInfluencer createInfluencer() { + @FXThread + protected @NotNull ParticleInfluencer createInfluencer() { return new RotationInfluencer(); } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreateSizeParticleInfluencerAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreateSizeParticleInfluencerAction.java index 77bf7d8e..d6bddb2c 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreateSizeParticleInfluencerAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreateSizeParticleInfluencerAction.java @@ -1,11 +1,10 @@ package com.ss.editor.ui.control.model.tree.action.particle.emitter.toneg0d.influerencer; +import com.ss.editor.annotation.FXThread; import com.ss.editor.model.undo.editor.ModelChangeConsumer; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; - import org.jetbrains.annotations.NotNull; - import tonegod.emitter.Messages; import tonegod.emitter.ParticleEmitterNode; import tonegod.emitter.influencers.ParticleInfluencer; @@ -18,25 +17,21 @@ */ public class CreateSizeParticleInfluencerAction extends AbstractCreateParticleInfluencerAction { - /** - * Instantiates a new Create size particle influencer action. - * - * @param nodeTree the node tree - * @param node the node - */ - public CreateSizeParticleInfluencerAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { + + public CreateSizeParticleInfluencerAction(@NotNull final NodeTree nodeTree, + @NotNull final TreeNode node) { super(nodeTree, node); } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.PARTICLE_INFLUENCER_SIZE; } - @NotNull @Override - protected ParticleInfluencer createInfluencer() { + @FXThread + protected @NotNull ParticleInfluencer createInfluencer() { return new SizeInfluencer(); } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreateSpriteParticleInfluencerAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreateSpriteParticleInfluencerAction.java index 74e5ade2..38ff9a22 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreateSpriteParticleInfluencerAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/CreateSpriteParticleInfluencerAction.java @@ -1,11 +1,10 @@ package com.ss.editor.ui.control.model.tree.action.particle.emitter.toneg0d.influerencer; +import com.ss.editor.annotation.FXThread; import com.ss.editor.model.undo.editor.ModelChangeConsumer; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; - import org.jetbrains.annotations.NotNull; - import tonegod.emitter.Messages; import tonegod.emitter.ParticleEmitterNode; import tonegod.emitter.influencers.ParticleInfluencer; @@ -18,25 +17,20 @@ */ public class CreateSpriteParticleInfluencerAction extends AbstractCreateParticleInfluencerAction { - /** - * Instantiates a new Create sprite particle influencer action. - * - * @param nodeTree the node tree - * @param node the node - */ - public CreateSpriteParticleInfluencerAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { + public CreateSpriteParticleInfluencerAction(@NotNull final NodeTree nodeTree, + @NotNull final TreeNode node) { super(nodeTree, node); } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.PARTICLE_INFLUENCER_SPRITE; } - @NotNull @Override - protected ParticleInfluencer createInfluencer() { + @FXThread + protected @NotNull ParticleInfluencer createInfluencer() { return new SpriteInfluencer(); } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/RemoveParticleInfluencerAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/RemoveParticleInfluencerAction.java index 1001f025..7ddeb028 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/RemoveParticleInfluencerAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/influerencer/RemoveParticleInfluencerAction.java @@ -37,12 +37,14 @@ public RemoveParticleInfluencerAction(@NotNull final NodeTree nodeTree, @NotN super(nodeTree, node); } + @FXThread @Nullable @Override protected Image getIcon() { return Icons.REMOVE_12; } + @FXThread @NotNull @Override protected String getName() { diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/mesh/AbstractCreateParticleMeshAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/mesh/AbstractCreateParticleMeshAction.java index fc58700a..fba92d34 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/mesh/AbstractCreateParticleMeshAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/mesh/AbstractCreateParticleMeshAction.java @@ -35,6 +35,7 @@ public abstract class AbstractCreateParticleMeshAction extends AbstractNodeActio super(nodeTree, node); } + @FXThread @Nullable @Override protected Image getIcon() { diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/mesh/CreateImpostorParticleMeshAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/mesh/CreateImpostorParticleMeshAction.java index 7dbc2390..66a8e99a 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/mesh/CreateImpostorParticleMeshAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/mesh/CreateImpostorParticleMeshAction.java @@ -1,6 +1,7 @@ package com.ss.editor.ui.control.model.tree.action.particle.emitter.toneg0d.mesh; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; @@ -30,12 +31,14 @@ public CreateImpostorParticleMeshAction(@NotNull final NodeTree nodeTree, @No super(nodeTree, node); } + @FXThread @Nullable @Override protected Image getIcon() { return Icons.IMPOSTOR_16; } + @FXThread @NotNull @Override protected String getName() { diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/mesh/CreatePointParticleMeshAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/mesh/CreatePointParticleMeshAction.java index dc767efc..573b1c23 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/mesh/CreatePointParticleMeshAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/mesh/CreatePointParticleMeshAction.java @@ -1,6 +1,7 @@ package com.ss.editor.ui.control.model.tree.action.particle.emitter.toneg0d.mesh; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; @@ -30,12 +31,14 @@ public CreatePointParticleMeshAction(@NotNull final NodeTree nodeTree, @NotNu super(nodeTree, node); } + @FXThread @Nullable @Override protected Image getIcon() { return Icons.POINTS_16; } + @FXThread @NotNull @Override protected String getName() { diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/mesh/CreateQuadParticleMeshAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/mesh/CreateQuadParticleMeshAction.java index 88678b82..0ae863f4 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/mesh/CreateQuadParticleMeshAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/mesh/CreateQuadParticleMeshAction.java @@ -1,6 +1,7 @@ package com.ss.editor.ui.control.model.tree.action.particle.emitter.toneg0d.mesh; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; @@ -30,12 +31,14 @@ public CreateQuadParticleMeshAction(@NotNull final NodeTree nodeTree, @NotNul super(nodeTree, node); } + @FXThread @Nullable @Override protected Image getIcon() { return Icons.QUAD_16; } + @FXThread @NotNull @Override protected String getName() { diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/mesh/LoadModelParticlesMeshAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/mesh/LoadModelParticlesMeshAction.java index a7369e3d..fb637d59 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/mesh/LoadModelParticlesMeshAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/mesh/LoadModelParticlesMeshAction.java @@ -61,12 +61,14 @@ public LoadModelParticlesMeshAction(@NotNull final NodeTree nodeTree, @NotNul super(nodeTree, node); } + @FXThread @Nullable @Override protected Image getIcon() { return Icons.OPEN_FILE_16; } + @FXThread @NotNull @Override protected String getName() { diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/shape/AbstractCreateShapeEmitterAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/shape/AbstractCreateShapeEmitterAction.java index 0072fa7e..e81fb91b 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/shape/AbstractCreateShapeEmitterAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/shape/AbstractCreateShapeEmitterAction.java @@ -8,9 +8,9 @@ import com.ss.editor.plugin.api.dialog.GenericFactoryDialog; import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.editor.ui.Icons; -import com.ss.editor.ui.control.tree.action.AbstractNodeAction; import com.ss.editor.ui.control.model.tree.action.operation.particle.emitter.toneg0d.ChangeEmitterMeshOperation; import com.ss.editor.ui.control.tree.NodeTree; +import com.ss.editor.ui.control.tree.action.AbstractNodeAction; import com.ss.editor.ui.control.tree.node.TreeNode; import com.ss.rlib.util.VarTable; import com.ss.rlib.util.array.Array; @@ -26,25 +26,18 @@ */ public abstract class AbstractCreateShapeEmitterAction extends AbstractNodeAction { - /** - * Instantiates a new Abstract create shape emitter action. - * - * @param nodeTree the node tree - * @param node the node - */ - public AbstractCreateShapeEmitterAction(@NotNull final NodeTree nodeTree, - @NotNull final TreeNode node) { + public AbstractCreateShapeEmitterAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.GEOMETRY_16; } - @FXThread @Override + @FXThread protected void process() { super.process(); final GenericFactoryDialog dialog = new GenericFactoryDialog(getPropertyDefinitions(), this::handleResult); @@ -57,14 +50,15 @@ protected void process() { * * @return the dialog title. */ - @NotNull - protected abstract String getDialogTitle(); + @FXThread + protected abstract @NotNull String getDialogTitle(); /** * Handle the result from the dialog. * * @param vars the table with variables. */ + @FXThread private void handleResult(@NotNull final VarTable vars) { final TreeNode treeNode = getNode(); @@ -81,8 +75,8 @@ private void handleResult(@NotNull final VarTable vars) { * * @return the list of definitions. */ - @NotNull - protected abstract Array getPropertyDefinitions(); + @FXThread + protected abstract @NotNull Array getPropertyDefinitions(); /** * Create a mesh. @@ -90,6 +84,6 @@ private void handleResult(@NotNull final VarTable vars) { * @param vars the table with variables. * @return the mesh */ - @NotNull - protected abstract Mesh createMesh(@NotNull final VarTable vars); + @FXThread + protected abstract @NotNull Mesh createMesh(@NotNull final VarTable vars); } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/shape/CreateBoxShapeEmitterAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/shape/CreateBoxShapeEmitterAction.java index 0946e132..ffe1e952 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/shape/CreateBoxShapeEmitterAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/shape/CreateBoxShapeEmitterAction.java @@ -5,6 +5,7 @@ import com.jme3.scene.Mesh; import com.jme3.scene.shape.Box; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; @@ -27,31 +28,25 @@ public class CreateBoxShapeEmitterAction extends AbstractCreateShapeEmitterActio @NotNull private static final String PROPERTY_SIZE = "size"; - /** - * Instantiates a new Create box shape emitter action. - * - * @param nodeTree the node tree - * @param node the node - */ public CreateBoxShapeEmitterAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.CUBE_16; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_BOX_SHAPE; } - @NotNull @Override - protected Array getPropertyDefinitions() { + @FXThread + protected @NotNull Array getPropertyDefinitions() { final Array definitions = ArrayFactory.newArray(PropertyDefinition.class); definitions.add(new PropertyDefinition(VECTOR_3F, Messages.MODEL_PROPERTY_SIZE, PROPERTY_SIZE, new Vector3f(1, 1, 1))); @@ -59,15 +54,15 @@ protected Array getPropertyDefinitions() { return definitions; } - @NotNull @Override - protected String getDialogTitle() { + @FXThread + protected @NotNull String getDialogTitle() { return Messages.CREATE_PARTICLE_EMITTER_BOX_SHAPE_DIALOG_TITLE; } - @NotNull @Override - protected Mesh createMesh(@NotNull final VarTable vars) { + @FXThread + protected @NotNull Mesh createMesh(@NotNull final VarTable vars) { final Vector3f size = vars.get(PROPERTY_SIZE); return new Box(size.getX(), size.getY(), size.getZ()); } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/shape/CreateCylinderShapeEmitterAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/shape/CreateCylinderShapeEmitterAction.java index 2c5cf28d..2d331ddd 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/shape/CreateCylinderShapeEmitterAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/shape/CreateCylinderShapeEmitterAction.java @@ -5,10 +5,11 @@ import com.jme3.scene.Mesh; import com.jme3.scene.shape.Cylinder; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; -import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.rlib.util.VarTable; import com.ss.rlib.util.array.Array; import com.ss.rlib.util.array.ArrayFactory; @@ -36,31 +37,25 @@ public class CreateCylinderShapeEmitterAction extends AbstractCreateShapeEmitter @NotNull private static final String PROPERTY_HEIGHT = "height"; - /** - * Instantiates a new Create cylinder shape emitter action. - * - * @param nodeTree the node tree - * @param node the node - */ public CreateCylinderShapeEmitterAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.CYLINDER_16; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_CYLINDER_SHAPE; } - @NotNull @Override - protected Array getPropertyDefinitions() { + @FXThread + protected @NotNull Array getPropertyDefinitions() { final Array definitions = ArrayFactory.newArray(PropertyDefinition.class); definitions.add(new PropertyDefinition(INTEGER, Messages.MODEL_PROPERTY_AXIS_SAMPLES, PROPERTY_AXIS_SAMPLES, 8)); @@ -71,15 +66,15 @@ protected Array getPropertyDefinitions() { return definitions; } - @NotNull @Override - protected String getDialogTitle() { + @FXThread + protected @NotNull String getDialogTitle() { return Messages.CREATE_PARTICLE_EMITTER_CYLINDER_SHAPE_DIALOG_TITLE; } - @NotNull @Override - protected Mesh createMesh(@NotNull final VarTable vars) { + @FXThread + protected @NotNull Mesh createMesh(@NotNull final VarTable vars) { final int axisSamples = vars.getInteger(PROPERTY_AXIS_SAMPLES); final int radialSamples = vars.getInteger(PROPERTY_RADIAL_SAMPLES); final float radius = vars.getFloat(PROPERTY_RADIUS); diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/shape/CreateDomeShapeEmitterAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/shape/CreateDomeShapeEmitterAction.java index 4770c0b0..19d875ac 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/shape/CreateDomeShapeEmitterAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/shape/CreateDomeShapeEmitterAction.java @@ -5,10 +5,11 @@ import com.jme3.scene.Mesh; import com.jme3.scene.shape.Dome; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; -import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.rlib.util.VarTable; import com.ss.rlib.util.array.Array; import com.ss.rlib.util.array.ArrayFactory; @@ -33,31 +34,25 @@ public class CreateDomeShapeEmitterAction extends AbstractCreateShapeEmitterActi @NotNull private static final String PROPERTY_RADIUS = "radius"; - /** - * Instantiates a new Create dome shape emitter action. - * - * @param nodeTree the node tree - * @param node the node - */ public CreateDomeShapeEmitterAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.DOME_16; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_DOME_SHAPE; } - @NotNull @Override - protected Array getPropertyDefinitions() { + @FXThread + protected @NotNull Array getPropertyDefinitions() { final Array definitions = ArrayFactory.newArray(PropertyDefinition.class); definitions.add(new PropertyDefinition(INTEGER, Messages.MODEL_PROPERTY_PLANES, PROPERTY_PLANES, 10)); @@ -67,15 +62,15 @@ protected Array getPropertyDefinitions() { return definitions; } - @NotNull @Override - protected String getDialogTitle() { + @FXThread + protected @NotNull String getDialogTitle() { return Messages.CREATE_PARTICLE_EMITTER_DOME_SHAPE_DIALOG_TITLE; } - @NotNull @Override - protected Mesh createMesh(@NotNull final VarTable vars) { + @FXThread + protected @NotNull Mesh createMesh(@NotNull final VarTable vars) { final int planes = vars.getInteger(PROPERTY_PLANES); final int radialSamples = vars.getInteger(PROPERTY_RADIAL_SAMPLES); final float radius = vars.getFloat(PROPERTY_RADIUS); diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/shape/CreateQuadShapeEmitterAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/shape/CreateQuadShapeEmitterAction.java index 8092e098..c713fd4c 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/shape/CreateQuadShapeEmitterAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/shape/CreateQuadShapeEmitterAction.java @@ -5,6 +5,7 @@ import com.jme3.scene.Mesh; import com.jme3.scene.shape.Quad; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; @@ -33,31 +34,25 @@ public class CreateQuadShapeEmitterAction extends AbstractCreateShapeEmitterActi @NotNull private static final String PROPERTY_FLIP_COORDS = "flipCoords"; - /** - * Instantiates a new Create quad shape emitter action. - * - * @param nodeTree the node tree - * @param node the node - */ public CreateQuadShapeEmitterAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.QUAD_16; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_QUAD_SHAPE; } - @NotNull @Override - protected Array getPropertyDefinitions() { + @FXThread + protected @NotNull Array getPropertyDefinitions() { final Array definitions = ArrayFactory.newArray(PropertyDefinition.class); definitions.add(new PropertyDefinition(FLOAT, Messages.MODEL_PROPERTY_WIDTH, PROPERTY_WIDTH, 1F)); @@ -67,15 +62,15 @@ protected Array getPropertyDefinitions() { return definitions; } - @NotNull @Override - protected String getDialogTitle() { + @FXThread + protected @NotNull String getDialogTitle() { return Messages.CREATE_PARTICLE_EMITTER_QUAD_SHAPE_DIALOG_TITLE; } - @NotNull @Override - protected Mesh createMesh(@NotNull final VarTable vars) { + @FXThread + protected @NotNull Mesh createMesh(@NotNull final VarTable vars) { final float width = vars.getFloat(PROPERTY_WIDTH); final float height = vars.getFloat(PROPERTY_HEIGHT); final boolean flipCoords = vars.getBoolean(PROPERTY_FLIP_COORDS); diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/shape/CreateSphereShapeEmitterAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/shape/CreateSphereShapeEmitterAction.java index f0ef38eb..12b92916 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/shape/CreateSphereShapeEmitterAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/shape/CreateSphereShapeEmitterAction.java @@ -5,10 +5,11 @@ import com.jme3.scene.Mesh; import com.jme3.scene.shape.Sphere; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; -import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.rlib.util.VarTable; import com.ss.rlib.util.array.Array; import com.ss.rlib.util.array.ArrayFactory; @@ -33,32 +34,25 @@ public class CreateSphereShapeEmitterAction extends AbstractCreateShapeEmitterAc @NotNull private static final String PROPERTY_RADIUS = "radius"; - /** - * Instantiates a new Create sphere shape emitter action. - * - * @param nodeTree the node tree - * @param node the node - */ - public CreateSphereShapeEmitterAction(@NotNull final NodeTree nodeTree, - @NotNull final TreeNode node) { + public CreateSphereShapeEmitterAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.SPHERE_16; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_SPHERE_SHAPE; } - @NotNull @Override - protected Array getPropertyDefinitions() { + @FXThread + protected @NotNull Array getPropertyDefinitions() { final Array definitions = ArrayFactory.newArray(PropertyDefinition.class); definitions.add(new PropertyDefinition(INTEGER, Messages.MODEL_PROPERTY_Z_SAMPLES, PROPERTY_Z_SAMPLES, 10)); @@ -68,15 +62,15 @@ protected Array getPropertyDefinitions() { return definitions; } - @NotNull @Override - protected String getDialogTitle() { + @FXThread + protected @NotNull String getDialogTitle() { return Messages.CREATE_PARTICLE_EMITTER_SPHERE_SHAPE_DIALOG_TITLE; } - @NotNull @Override - protected Mesh createMesh(@NotNull final VarTable vars) { + @FXThread + protected @NotNull Mesh createMesh(@NotNull final VarTable vars) { final int zSamples = vars.getInteger(PROPERTY_Z_SAMPLES); final int radialSamples = vars.getInteger(PROPERTY_RADIAL_SAMPLES); final float radius = vars.getFloat(PROPERTY_RADIUS); diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/shape/CreateTorusShapeEmitterAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/shape/CreateTorusShapeEmitterAction.java index b36c6969..b7de7a10 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/shape/CreateTorusShapeEmitterAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/shape/CreateTorusShapeEmitterAction.java @@ -5,10 +5,11 @@ import com.jme3.scene.Mesh; import com.jme3.scene.shape.Torus; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; -import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.rlib.util.VarTable; import com.ss.rlib.util.array.Array; import com.ss.rlib.util.array.ArrayFactory; @@ -36,31 +37,25 @@ public class CreateTorusShapeEmitterAction extends AbstractCreateShapeEmitterAct @NotNull private static final String PROPERTY_OUTER_RADIUS = "outerRadius"; - /** - * Instantiates a new Create torus shape emitter action. - * - * @param nodeTree the node tree - * @param node the node - */ public CreateTorusShapeEmitterAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.TORUS_16; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_TORUS_SHAPE; } - @NotNull @Override - protected Array getPropertyDefinitions() { + @FXThread + protected @NotNull Array getPropertyDefinitions() { final Array definitions = ArrayFactory.newArray(PropertyDefinition.class); definitions.add(new PropertyDefinition(INTEGER, Messages.MODEL_PROPERTY_CIRCLE_SAMPLES, PROPERTY_CIRCLE_SAMPLES, 10)); @@ -71,15 +66,15 @@ protected Array getPropertyDefinitions() { return definitions; } - @NotNull @Override - protected String getDialogTitle() { + @FXThread + protected @NotNull String getDialogTitle() { return Messages.CREATE_PARTICLE_EMITTER_TORUS_SHAPE_DIALOG_TITLE; } - @NotNull @Override - protected Mesh createMesh(@NotNull final VarTable vars) { + @FXThread + protected @NotNull Mesh createMesh(@NotNull final VarTable vars) { final int circleSamples = vars.getInteger(PROPERTY_CIRCLE_SAMPLES); final int radialSamples = vars.getInteger(PROPERTY_RADIAL_SAMPLES); final float innerRadius = vars.getFloat(PROPERTY_INNER_RADIUS); diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/shape/CreateTriangleShapeEmitterAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/shape/CreateTriangleShapeEmitterAction.java index 5f40f9bc..8967b5b8 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/shape/CreateTriangleShapeEmitterAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/shape/CreateTriangleShapeEmitterAction.java @@ -3,10 +3,11 @@ import static com.ss.editor.extension.property.EditablePropertyType.FLOAT; import com.jme3.scene.Mesh; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; -import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.rlib.util.VarTable; import com.ss.rlib.util.array.Array; import com.ss.rlib.util.array.ArrayFactory; @@ -26,31 +27,25 @@ public class CreateTriangleShapeEmitterAction extends AbstractCreateShapeEmitter @NotNull private static final String PROPERTY_SIZE = "size"; - /** - * Instantiates a new Create triangle shape emitter action. - * - * @param nodeTree the node tree - * @param node the node - */ public CreateTriangleShapeEmitterAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.TRIANGLE_16; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_TRIANGLE_SHAPE; } - @NotNull @Override - protected Array getPropertyDefinitions() { + @FXThread + protected @NotNull Array getPropertyDefinitions() { final Array definitions = ArrayFactory.newArray(PropertyDefinition.class); definitions.add(new PropertyDefinition(FLOAT, Messages.MODEL_PROPERTY_SIZE, PROPERTY_SIZE, 1F)); @@ -58,15 +53,15 @@ protected Array getPropertyDefinitions() { return definitions; } - @NotNull @Override - protected String getDialogTitle() { + @FXThread + protected @NotNull String getDialogTitle() { return Messages.CREATE_PARTICLE_EMITTER_TRIANGLE_SHAPE_DIALOG_TITLE; } - @NotNull @Override - protected Mesh createMesh(@NotNull final VarTable vars) { + @FXThread + protected @NotNull Mesh createMesh(@NotNull final VarTable vars) { final float size = vars.getFloat(PROPERTY_SIZE); return new TriangleEmitterShape(size); } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/shape/LoadModelShapeEmitterAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/shape/LoadModelShapeEmitterAction.java index 57986cf7..0c1ac344 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/shape/LoadModelShapeEmitterAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/particle/emitter/toneg0d/shape/LoadModelShapeEmitterAction.java @@ -47,6 +47,7 @@ public class LoadModelShapeEmitterAction extends AbstractNodeAction nodeTree, @NotNull super(nodeTree, node); } + @FXThread @NotNull @Override protected String getName() { diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/physics/shape/AbstractCreateShapeAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/physics/shape/AbstractCreateShapeAction.java index 958a244c..ffaf877f 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/physics/shape/AbstractCreateShapeAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/physics/shape/AbstractCreateShapeAction.java @@ -9,9 +9,9 @@ import com.ss.editor.plugin.api.dialog.GenericFactoryDialog; import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.editor.ui.Icons; -import com.ss.editor.ui.control.tree.action.AbstractNodeAction; import com.ss.editor.ui.control.model.tree.action.operation.ChangeCollisionShapeOperation; import com.ss.editor.ui.control.tree.NodeTree; +import com.ss.editor.ui.control.tree.action.AbstractNodeAction; import com.ss.editor.ui.control.tree.node.TreeNode; import com.ss.rlib.util.VarTable; import com.ss.rlib.util.array.Array; @@ -26,24 +26,18 @@ */ public abstract class AbstractCreateShapeAction extends AbstractNodeAction { - /** - * Instantiates a new Abstract create shape action. - * - * @param nodeTree the node tree - * @param node the node - */ AbstractCreateShapeAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.GEOMETRY_16; } - @FXThread @Override + @FXThread protected void process() { super.process(); @@ -60,14 +54,15 @@ protected void process() { * * @return the dialog title. */ - @NotNull - protected abstract String getDialogTitle(); + @FXThread + protected abstract @NotNull String getDialogTitle(); /** * Handle the result from the dialog. * * @param vars the table with variables. */ + @FXThread private void handleResult(@NotNull final VarTable vars) { final TreeNode treeNode = getNode(); @@ -85,8 +80,8 @@ private void handleResult(@NotNull final VarTable vars) { * * @return the list of definitions. */ - @NotNull - protected abstract Array getPropertyDefinitions(); + @FXThread + protected abstract @NotNull Array getPropertyDefinitions(); /** * Create a collision shape. @@ -94,6 +89,6 @@ private void handleResult(@NotNull final VarTable vars) { * @param vars the table with variables. * @return the collision shape */ - @NotNull - protected abstract CollisionShape createShape(@NotNull final VarTable vars); + @FXThread + protected abstract @NotNull CollisionShape createShape(@NotNull final VarTable vars); } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/physics/shape/CreateBoxCollisionShapeAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/physics/shape/CreateBoxCollisionShapeAction.java index 200a314c..7b6426b0 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/physics/shape/CreateBoxCollisionShapeAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/physics/shape/CreateBoxCollisionShapeAction.java @@ -5,6 +5,7 @@ import com.jme3.bullet.collision.shapes.CollisionShape; import com.jme3.math.Vector3f; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; import com.ss.editor.plugin.api.property.PropertyDefinition; @@ -23,40 +24,36 @@ public class CreateBoxCollisionShapeAction extends AbstractCreateShapeAction { @NotNull private static final String PROPERTY_HALF_EXTENTS = "halfExtents"; - /** - * Instantiates a new Create box collision shape action. - * - * @param nodeTree the node tree - * @param node the node - */ public CreateBoxCollisionShapeAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_BOX_COLLISION_SHAPE; } - @NotNull @Override - protected String getDialogTitle() { + @FXThread + protected @NotNull String getDialogTitle() { return Messages.CREATE_BOX_COLLISION_SHAPE_DIALOG_TITLE; } - @NotNull + @Override - protected Array getPropertyDefinitions() { + @FXThread + protected @NotNull Array getPropertyDefinitions() { final Array definitions = ArrayFactory.newArray(PropertyDefinition.class); definitions.add(new PropertyDefinition(VECTOR_3F, Messages.MODEL_PROPERTY_HALF_EXTENTS, PROPERTY_HALF_EXTENTS, new Vector3f(1, 1, 1))); return definitions; } - @NotNull + @Override - protected CollisionShape createShape(@NotNull final VarTable vars) { + @FXThread + protected @NotNull CollisionShape createShape(@NotNull final VarTable vars) { final Vector3f halfExtents = vars.get(PROPERTY_HALF_EXTENTS); return new BoxCollisionShape(halfExtents); } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/physics/shape/CreateCapsuleCollisionShapeAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/physics/shape/CreateCapsuleCollisionShapeAction.java index 0fa8362f..0b49e14b 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/physics/shape/CreateCapsuleCollisionShapeAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/physics/shape/CreateCapsuleCollisionShapeAction.java @@ -4,6 +4,7 @@ import com.jme3.bullet.collision.shapes.CapsuleCollisionShape; import com.jme3.bullet.collision.shapes.CollisionShape; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; @@ -28,47 +29,41 @@ public class CreateCapsuleCollisionShapeAction extends AbstractCreateShapeAction @NotNull private static final String PROPERTY_HEIGHT = "height"; - /** - * Instantiates a new Create capsule collision shape action. - * - * @param nodeTree the node tree - * @param node the node - */ public CreateCapsuleCollisionShapeAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.CAPSULE_16; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_CAPSULE_COLLISION_SHAPE; } - @NotNull @Override - protected String getDialogTitle() { + @FXThread + protected @NotNull String getDialogTitle() { return Messages.CREATE_CAPSULE_COLLISION_SHAPE_DIALOG_TITLE; } - @NotNull @Override - protected Array getPropertyDefinitions() { + @FXThread + protected @NotNull Array getPropertyDefinitions() { final Array definitions = ArrayFactory.newArray(PropertyDefinition.class); definitions.add(new PropertyDefinition(FLOAT, Messages.MODEL_PROPERTY_RADIUS, PROPERTY_RADIUS, 1F)); definitions.add(new PropertyDefinition(FLOAT, Messages.MODEL_PROPERTY_HEIGHT, PROPERTY_HEIGHT, 1F)); return definitions; } - @NotNull @Override - protected CollisionShape createShape(@NotNull final VarTable vars) { + @FXThread + protected @NotNull CollisionShape createShape(@NotNull final VarTable vars) { final float height = vars.getFloat(PROPERTY_HEIGHT); final float radius = vars.getFloat(PROPERTY_RADIUS); return new CapsuleCollisionShape(radius, height); diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/physics/shape/CreateConeCollisionShapeAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/physics/shape/CreateConeCollisionShapeAction.java index ac7ac104..a232df54 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/physics/shape/CreateConeCollisionShapeAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/physics/shape/CreateConeCollisionShapeAction.java @@ -5,10 +5,11 @@ import com.jme3.bullet.collision.shapes.CollisionShape; import com.jme3.bullet.collision.shapes.ConeCollisionShape; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; -import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.rlib.util.VarTable; import com.ss.rlib.util.array.Array; import com.ss.rlib.util.array.ArrayFactory; @@ -38,38 +39,31 @@ protected enum Axis { @NotNull private static final String PROPERTY_AXIS = "axis"; - /** - * Instantiates a new Create cone collision shape action. - * - * @param nodeTree the node tree - * @param node the node - */ - public CreateConeCollisionShapeAction(@NotNull final NodeTree nodeTree, - @NotNull final TreeNode node) { + public CreateConeCollisionShapeAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.CONE_16; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_CONE_COLLISION_SHAPE; } - @NotNull @Override - protected String getDialogTitle() { + @FXThread + protected @NotNull String getDialogTitle() { return Messages.CREATE_CONE_COLLISION_SHAPE_DIALOG_TITLE; } - @NotNull @Override - protected Array getPropertyDefinitions() { + @FXThread + protected @NotNull Array getPropertyDefinitions() { final Array definitions = ArrayFactory.newArray(PropertyDefinition.class); definitions.add(new PropertyDefinition(FLOAT, Messages.MODEL_PROPERTY_RADIUS, PROPERTY_RADIUS, 1F)); definitions.add(new PropertyDefinition(FLOAT, Messages.MODEL_PROPERTY_HEIGHT, PROPERTY_HEIGHT, 1F)); @@ -77,9 +71,9 @@ protected Array getPropertyDefinitions() { return definitions; } - @NotNull @Override - protected CollisionShape createShape(@NotNull final VarTable vars) { + @FXThread + protected @NotNull CollisionShape createShape(@NotNull final VarTable vars) { final float height = vars.getFloat(PROPERTY_HEIGHT); final float radius = vars.getFloat(PROPERTY_RADIUS); diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/physics/shape/CreateCylinderCollisionShapeAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/physics/shape/CreateCylinderCollisionShapeAction.java index 678db1c1..14a9876b 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/physics/shape/CreateCylinderCollisionShapeAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/physics/shape/CreateCylinderCollisionShapeAction.java @@ -6,11 +6,12 @@ import com.jme3.bullet.collision.shapes.CylinderCollisionShape; import com.jme3.math.Vector3f; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.model.tree.action.physics.shape.CreateConeCollisionShapeAction.Axis; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; -import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.rlib.util.VarTable; import com.ss.rlib.util.array.Array; import com.ss.rlib.util.array.ArrayFactory; @@ -31,47 +32,40 @@ public class CreateCylinderCollisionShapeAction extends AbstractCreateShapeActio @NotNull private static final String PROPERTY_AXIS = "axis"; - /** - * Instantiates a new Create cylinder collision shape action. - * - * @param nodeTree the node tree - * @param node the node - */ - public CreateCylinderCollisionShapeAction(@NotNull final NodeTree nodeTree, - @NotNull final TreeNode node) { + public CreateCylinderCollisionShapeAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.CYLINDER_16; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_CYLINDER_COLLISION_SHAPE; } - @NotNull @Override - protected String getDialogTitle() { + @FXThread + protected @NotNull String getDialogTitle() { return Messages.CREATE_CYLINDER_COLLISION_SHAPE_DIALOG_TITLE; } - @NotNull @Override - protected Array getPropertyDefinitions() { + @FXThread + protected @NotNull Array getPropertyDefinitions() { final Array definitions = ArrayFactory.newArray(PropertyDefinition.class); definitions.add(new PropertyDefinition(VECTOR_3F, Messages.MODEL_PROPERTY_HALF_EXTENTS, PROPERTY_HALF_EXTENTS, new Vector3f(1, 1, 1))); definitions.add(new PropertyDefinition(ENUM, Messages.MODEL_PROPERTY_AXIS, PROPERTY_AXIS, Axis.X)); return definitions; } - @NotNull @Override - protected CollisionShape createShape(@NotNull final VarTable vars) { + @FXThread + protected @NotNull CollisionShape createShape(@NotNull final VarTable vars) { final Vector3f halfExtents = vars.get(PROPERTY_HALF_EXTENTS); final Axis axis = vars.get(PROPERTY_AXIS); return new CylinderCollisionShape(halfExtents, axis.ordinal()); diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/physics/shape/CreateSphereCollisionShapeAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/physics/shape/CreateSphereCollisionShapeAction.java index a42ac34d..a661231c 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/physics/shape/CreateSphereCollisionShapeAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/physics/shape/CreateSphereCollisionShapeAction.java @@ -4,10 +4,11 @@ import com.jme3.bullet.collision.shapes.CollisionShape; import com.jme3.bullet.collision.shapes.SphereCollisionShape; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; -import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.rlib.util.VarTable; import com.ss.rlib.util.array.Array; import com.ss.rlib.util.array.ArrayFactory; @@ -25,46 +26,39 @@ public class CreateSphereCollisionShapeAction extends AbstractCreateShapeAction @NotNull private static final String PROPERTY_RADIUS = "radius"; - /** - * Instantiates a new Create sphere collision shape action. - * - * @param nodeTree the node tree - * @param node the node - */ - public CreateSphereCollisionShapeAction(@NotNull final NodeTree nodeTree, - @NotNull final TreeNode node) { + public CreateSphereCollisionShapeAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.SPHERE_16; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_SPHERE_COLLISION_SHAPE; } - @NotNull @Override - protected String getDialogTitle() { + @FXThread + protected @NotNull String getDialogTitle() { return Messages.CREATE_SPHERE_COLLISION_SHAPE_DIALOG_TITLE; } - @NotNull @Override - protected Array getPropertyDefinitions() { + @FXThread + protected @NotNull Array getPropertyDefinitions() { final Array definitions = ArrayFactory.newArray(PropertyDefinition.class); definitions.add(new PropertyDefinition(FLOAT, Messages.MODEL_PROPERTY_RADIUS, PROPERTY_RADIUS, 1F)); return definitions; } - @NotNull @Override - protected CollisionShape createShape(@NotNull final VarTable vars) { + @FXThread + protected @NotNull CollisionShape createShape(@NotNull final VarTable vars) { final float radius = vars.get(PROPERTY_RADIUS); return new SphereCollisionShape(radius); } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/physics/shape/GenerateCollisionShapeAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/physics/shape/GenerateCollisionShapeAction.java index d7ff70c4..a3d82c8a 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/physics/shape/GenerateCollisionShapeAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/physics/shape/GenerateCollisionShapeAction.java @@ -17,11 +17,11 @@ import com.ss.editor.Messages; import com.ss.editor.annotation.FXThread; import com.ss.editor.model.undo.editor.ChangeConsumer; +import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.model.tree.action.operation.ChangeCollisionShapeOperation; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; -import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.rlib.util.VarTable; import com.ss.rlib.util.array.Array; import com.ss.rlib.util.array.ArrayFactory; @@ -39,24 +39,18 @@ public class GenerateCollisionShapeAction extends AbstractCreateShapeAction { @NotNull private static final Array EMPTY_DEFINITIONS = ArrayFactory.asArray(); - /** - * Instantiates a new Generate collision shape action. - * - * @param nodeTree the node tree - * @param node the node - */ public GenerateCollisionShapeAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.INFLUENCER_16; } - @FXThread @Override + @FXThread protected void process() { super.process(); @@ -97,27 +91,27 @@ protected void process() { changeConsumer.execute(new ChangeCollisionShapeOperation(shape, object.getCollisionShape(), object)); } - @NotNull @Override - protected String getDialogTitle() { + @FXThread + protected @NotNull String getDialogTitle() { throw new RuntimeException(); } - @NotNull @Override - protected Array getPropertyDefinitions() { + @FXThread + protected @NotNull Array getPropertyDefinitions() { return EMPTY_DEFINITIONS; } - @NotNull @Override - protected CollisionShape createShape(@NotNull final VarTable vars) { + @FXThread + protected @NotNull CollisionShape createShape(@NotNull final VarTable vars) { throw new RuntimeException(); } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_GENERATE_COLLISION_SHAPE; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/scene/CreateSceneLayerAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/scene/CreateSceneLayerAction.java index d16ecc6e..2b3edbe0 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/scene/CreateSceneLayerAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/scene/CreateSceneLayerAction.java @@ -2,23 +2,21 @@ import com.ss.editor.Messages; import com.ss.editor.annotation.FXThread; +import com.ss.editor.extension.scene.SceneLayer; +import com.ss.editor.extension.scene.SceneNode; import com.ss.editor.model.undo.editor.ModelChangeConsumer; import com.ss.editor.model.undo.editor.SceneChangeConsumer; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.layer.LayersRoot; import com.ss.editor.ui.control.layer.node.LayersRootTreeNode; -import com.ss.editor.ui.control.tree.action.AbstractNodeAction; import com.ss.editor.ui.control.model.tree.action.operation.scene.AddSceneLayerOperation; import com.ss.editor.ui.control.tree.NodeTree; +import com.ss.editor.ui.control.tree.action.AbstractNodeAction; import com.ss.editor.ui.control.tree.node.TreeNode; -import com.ss.editor.extension.scene.SceneLayer; -import com.ss.editor.extension.scene.SceneNode; - +import javafx.scene.image.Image; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import javafx.scene.image.Image; - /** * The action to create a scene layer. * @@ -26,30 +24,24 @@ */ public class CreateSceneLayerAction extends AbstractNodeAction { - /** - * Instantiates a new Create scene layer action. - * - * @param nodeTree the node tree - * @param node the node - */ public CreateSceneLayerAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.LAYERS_16; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_CREATE_LAYER; } - @FXThread @Override + @FXThread protected void process() { super.process(); diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/scene/RemoveSceneLayerAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/scene/RemoveSceneLayerAction.java index 95935310..f1fe73a4 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/scene/RemoveSceneLayerAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/scene/RemoveSceneLayerAction.java @@ -9,9 +9,9 @@ import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.layer.LayersRoot; import com.ss.editor.ui.control.layer.node.SceneLayerTreeNode; -import com.ss.editor.ui.control.tree.action.AbstractNodeAction; import com.ss.editor.ui.control.model.tree.action.operation.scene.RemoveSceneLayerOperation; import com.ss.editor.ui.control.tree.NodeTree; +import com.ss.editor.ui.control.tree.action.AbstractNodeAction; import com.ss.editor.ui.control.tree.node.TreeNode; import javafx.scene.image.Image; import org.jetbrains.annotations.NotNull; @@ -24,30 +24,24 @@ */ public class RemoveSceneLayerAction extends AbstractNodeAction { - /** - * Instantiates a new Remove scene layer action. - * - * @param nodeTree the node tree - * @param node the node - */ public RemoveSceneLayerAction(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { super(nodeTree, node); } - @Nullable @Override - protected Image getIcon() { + @FXThread + protected @Nullable Image getIcon() { return Icons.REMOVE_12; } - @NotNull @Override - protected String getName() { + @FXThread + protected @NotNull String getName() { return Messages.MODEL_NODE_TREE_ACTION_REMOVE; } - @FXThread @Override + @FXThread protected void process() { super.process(); diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/terrain/CreateTerrainAction.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/terrain/CreateTerrainAction.java index 4b82a90b..e44eb1f6 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/terrain/CreateTerrainAction.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/terrain/CreateTerrainAction.java @@ -29,12 +29,14 @@ public CreateTerrainAction(@NotNull final NodeTree nodeTree, @NotNull final T super(nodeTree, node); } + @FXThread @Nullable @Override protected Image getIcon() { return Icons.TERRAIN_16; } + @FXThread @NotNull @Override protected String getName() { diff --git a/src/main/java/com/ss/editor/ui/control/tree/action/AbstractNodeAction.java b/src/main/java/com/ss/editor/ui/control/tree/action/AbstractNodeAction.java index 77a62d04..a37937af 100644 --- a/src/main/java/com/ss/editor/ui/control/tree/action/AbstractNodeAction.java +++ b/src/main/java/com/ss/editor/ui/control/tree/action/AbstractNodeAction.java @@ -80,6 +80,7 @@ public AbstractNodeAction(@NotNull final NodeTree nodeTree, @NotNull final Tr * * @return the name of this action. */ + @FXThread protected abstract @NotNull String getName(); /** @@ -95,6 +96,7 @@ protected void process() { * * @return he icon or null. */ + @FXThread protected @Nullable Image getIcon() { return null; } @@ -104,6 +106,7 @@ protected void process() { * * @return the component of the model three. */ + @FXThread protected @NotNull NodeTree getNodeTree() { return nodeTree; } @@ -113,6 +116,7 @@ protected void process() { * * @return the node of the model. */ + @FXThread protected @NotNull TreeNode getNode() { return node; } From 36b5782fef31887de229d4e566930c1c405cbb95 Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Sun, 10 Sep 2017 11:22:18 +0300 Subject: [PATCH 10/50] refactoring --- src/main/java/com/ss/editor/Editor.java | 4 ++ .../converter/impl/AbstractFileConverter.java | 1 + .../ss/editor/manager/ClasspathManager.java | 2 + .../Advanced3DFileEditorWithRightTool.java | 18 +++++--- .../plugin/api/editor/BaseFileEditor.java | 1 + .../material/BaseMaterialEditor3DState.java | 10 ++++- .../api/file/creator/GenericFileCreator.java | 2 + .../api/property/PropertyDefinition.java | 27 +++++++----- .../AssetResourcePropertyEditorControl.java | 12 +++-- .../control/AwtFontPropertyEditorControl.java | 22 +++++++--- .../control/BooleanPropertyEditorControl.java | 11 ++++- .../control/ColorPropertyEditorControl.java | 8 +++- .../control/EnumPropertyEditorControl.java | 8 +++- .../control/FloatPropertyEditorControl.java | 12 ++++- .../GeometryAssetResourcePropertyControl.java | 5 ++- .../control/IntegerPropertyEditorControl.java | 12 ++++- .../control/PropertyEditorControl.java | 18 +++++--- .../control/PropertyEditorControlFactory.java | 44 +++++++++++++------ .../ResourcePropertyEditorControl.java | 12 ++++- .../SpatialAssetResourcePropertyControl.java | 12 +++-- .../StringFromListPropertyEditorControl.java | 20 +++++++-- .../control/StringPropertyEditorControl.java | 11 ++++- .../Vector3fPropertyEditorControl.java | 16 ++++--- .../creator/impl/AbstractFileCreator.java | 1 + .../editor/impl/AbstractFileEditor.java | 1 + .../impl/material/MaterialFileEditor.java | 8 ++-- .../impl/scene/AbstractSceneFileEditor.java | 8 ++-- .../model/property/ModelPropertyEditor.java | 3 ++ .../builder/impl/MaterialPropertyBuilder.java | 2 +- ...ctionLightElementModelPropertyControl.java | 2 + .../control/LayerModelPropertyControl.java | 6 +++ .../NodeElementModelPropertyControl.java | 2 + ...PointLightElementModelPropertyControl.java | 2 + .../SpatialElementModelPropertyControl.java | 2 + .../MaterialEmitterPropertyControl.java | 4 ++ .../action/operation/AddChildOperation.java | 12 ----- .../ui/control/property/PropertyControl.java | 19 +++++++- .../ui/control/property/PropertyEditor.java | 14 ++++++ .../impl/EditableObjectPropertyBuilder.java | 12 ++++- .../impl/AudioKeyPropertyControl.java | 39 +++++++++++----- .../property/impl/BooleanPropertyControl.java | 7 +++ .../property/impl/ColorPropertyControl.java | 8 ++++ .../property/impl/DefaultPropertyControl.java | 6 +++ .../impl/DefaultSinglePropertyControl.java | 4 ++ .../property/impl/ElementPropertyControl.java | 5 +++ .../property/impl/EnumPropertyControl.java | 11 ++++- .../property/impl/FloatPropertyControl.java | 6 +++ .../impl/IntArrayPropertyControl.java | 7 +++ .../property/impl/IntegerPropertyControl.java | 6 +++ .../impl/LodLevelPropertyControl.java | 19 ++++++-- .../impl/MaterialKeyPropertyControl.java | 5 +++ .../impl/MaterialPropertyControl.java | 36 ++++++++++----- .../property/impl/MinMaxPropertyControl.java | 3 ++ .../impl/QuaternionPropertyControl.java | 10 ++++- .../property/impl/StringPropertyControl.java | 7 +++ .../impl/Texture2DPropertyControl.java | 42 +++++++++++++++--- .../impl/Vector2FPropertyControl.java | 11 +++++ .../impl/Vector3FPropertyControl.java | 9 ++++ .../java/com/ss/editor/ui/util/UIUtils.java | 10 +++++ 59 files changed, 499 insertions(+), 138 deletions(-) diff --git a/src/main/java/com/ss/editor/Editor.java b/src/main/java/com/ss/editor/Editor.java index 90429654..56583bd6 100644 --- a/src/main/java/com/ss/editor/Editor.java +++ b/src/main/java/com/ss/editor/Editor.java @@ -333,6 +333,8 @@ public void simpleInitApp() { /** * Lock the render thread for doing actions with game scene. + * + * @return the lock stamp. */ public long syncLock() { return lock.writeLock(); @@ -340,6 +342,8 @@ public long syncLock() { /** * Unlock the render thread. + * + * @param stamp the stamp of the lock. */ public void syncUnlock(final long stamp) { lock.unlockWrite(stamp); diff --git a/src/main/java/com/ss/editor/file/converter/impl/AbstractFileConverter.java b/src/main/java/com/ss/editor/file/converter/impl/AbstractFileConverter.java index da875108..c1b27973 100644 --- a/src/main/java/com/ss/editor/file/converter/impl/AbstractFileConverter.java +++ b/src/main/java/com/ss/editor/file/converter/impl/AbstractFileConverter.java @@ -119,6 +119,7 @@ public void convert(@NotNull final Path source, @NotNull final Path destination) * @param source the source file. * @param destination the target file. * @param overwrite is need to overwrite. + * @throws IOException if was problem with writing to the destination or reading from the source file. */ protected void convertImpl(@NotNull final Path source, @NotNull final Path destination, final boolean overwrite) throws IOException { diff --git a/src/main/java/com/ss/editor/manager/ClasspathManager.java b/src/main/java/com/ss/editor/manager/ClasspathManager.java index 84dfa2fa..941dd31f 100644 --- a/src/main/java/com/ss/editor/manager/ClasspathManager.java +++ b/src/main/java/com/ss/editor/manager/ClasspathManager.java @@ -264,6 +264,8 @@ private void setClassesLoader(@Nullable final URLClassLoader classesLoader) { /** * Find all implementations of the interface class. * + * @param the type of an interface. + * @param interfaceClass the interface class. * @return the list of all available implementations. */ public @NotNull Array> findImplements(@NotNull final Class interfaceClass) { diff --git a/src/main/java/com/ss/editor/plugin/api/editor/Advanced3DFileEditorWithRightTool.java b/src/main/java/com/ss/editor/plugin/api/editor/Advanced3DFileEditorWithRightTool.java index be78ed18..c8d66d79 100644 --- a/src/main/java/com/ss/editor/plugin/api/editor/Advanced3DFileEditorWithRightTool.java +++ b/src/main/java/com/ss/editor/plugin/api/editor/Advanced3DFileEditorWithRightTool.java @@ -78,8 +78,8 @@ protected void createContent(@NotNull final StackPane root) { protected void createEditorAreaPane() { editorAreaPane = new StackPane(); - editorAreaPane.setOnDragOver(this::dragOver); - editorAreaPane.setOnDragDropped(this::dragDropped); + editorAreaPane.setOnDragOver(this::handleDragOverEvent); + editorAreaPane.setOnDragDropped(this::handleDragDroppedEvent); editor3DArea = new BorderPane(); editor3DArea.setOnMousePressed(event -> editor3DArea.requestFocus()); @@ -132,20 +132,24 @@ protected void loadState() { * Create and add tool components to the container. * * @param container the tool container. - * @param container the root. + * @param root the root. */ protected void createToolComponents(@NotNull final EditorToolComponent container, @NotNull final StackPane root) { } /** - * Handle drag objects. + * Handle drag over events. + * + * @param dragEvent the drag event. */ - protected void dragOver(@NotNull final DragEvent dragEvent) { + protected void handleDragOverEvent(@NotNull final DragEvent dragEvent) { } /** - * Handle dropped texture. + * Handle dropped events. + * + * @param dragEvent the drop event. */ - protected void dragDropped(@NotNull final DragEvent dragEvent) { + protected void handleDragDroppedEvent(@NotNull final DragEvent dragEvent) { } } diff --git a/src/main/java/com/ss/editor/plugin/api/editor/BaseFileEditor.java b/src/main/java/com/ss/editor/plugin/api/editor/BaseFileEditor.java index 6f60ebcd..7ba517c4 100644 --- a/src/main/java/com/ss/editor/plugin/api/editor/BaseFileEditor.java +++ b/src/main/java/com/ss/editor/plugin/api/editor/BaseFileEditor.java @@ -78,6 +78,7 @@ protected void loadState() { * Do main activities to open the file. * * @param file the file to open. + * @throws IOException if was some problem with writing to the file. */ @FXThread protected void doOpenFile(@NotNull final Path file) throws IOException { diff --git a/src/main/java/com/ss/editor/plugin/api/editor/material/BaseMaterialEditor3DState.java b/src/main/java/com/ss/editor/plugin/api/editor/material/BaseMaterialEditor3DState.java index c4203e47..69bb890c 100644 --- a/src/main/java/com/ss/editor/plugin/api/editor/material/BaseMaterialEditor3DState.java +++ b/src/main/java/com/ss/editor/plugin/api/editor/material/BaseMaterialEditor3DState.java @@ -172,6 +172,8 @@ public void updateMaterial(@NotNull final Material material) { /** * Update the {@link Material} in the {@link EditorThread}. + * + * @param material the new material. */ protected void updateMaterialImpl(@NotNull final Material material) { @@ -206,6 +208,8 @@ public void changeMode(@NotNull final ModelType modelType) { /** * Change the {@link ModelType} in the {@link EditorThread}. + * + * @param modelType the new model type. */ protected void changeModeImpl(@NotNull final ModelType modelType) { @@ -241,6 +245,8 @@ public void changeBucketType(@NotNull final Bucket bucket) { /** * Change the {@link Bucket} in the {@link EditorThread}. + * + * @param bucket the new bucket. */ protected void changeBucketTypeImpl(@NotNull final Bucket bucket) { @@ -314,8 +320,10 @@ public void updateLightEnabled(final boolean enabled) { /** * Update the light in the scene in the {@link EditorThread}. + * + * @param enabled true if light should be enabled. */ - protected void updateLightEnabledImpl(boolean enabled) { + protected void updateLightEnabledImpl(final boolean enabled) { if (enabled == isLightEnabled()) return; final DirectionalLight light = getLightForCamera(); diff --git a/src/main/java/com/ss/editor/plugin/api/file/creator/GenericFileCreator.java b/src/main/java/com/ss/editor/plugin/api/file/creator/GenericFileCreator.java index e7e298e4..846f3d2b 100644 --- a/src/main/java/com/ss/editor/plugin/api/file/creator/GenericFileCreator.java +++ b/src/main/java/com/ss/editor/plugin/api/file/creator/GenericFileCreator.java @@ -88,6 +88,7 @@ protected void validateFileName() { * Validate variables. * * @param vars the variables. + * @return true if the all variables are valid. */ @FXThread protected boolean validate(@NotNull final VarTable vars) { @@ -105,6 +106,7 @@ protected void writeData(@NotNull final Path resultFile) throws IOException { * * @param vars the available variables. * @param resultFile the result file. + * @throws IOException if was some problem with writing to the result file. */ @BackgroundThread protected void writeData(@NotNull final VarTable vars, @NotNull final Path resultFile) throws IOException { diff --git a/src/main/java/com/ss/editor/plugin/api/property/PropertyDefinition.java b/src/main/java/com/ss/editor/plugin/api/property/PropertyDefinition.java index f6e7c33e..9d862837 100644 --- a/src/main/java/com/ss/editor/plugin/api/property/PropertyDefinition.java +++ b/src/main/java/com/ss/editor/plugin/api/property/PropertyDefinition.java @@ -1,5 +1,6 @@ package com.ss.editor.plugin.api.property; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.extension.property.EditablePropertyType; import com.ss.rlib.util.array.Array; import com.ss.rlib.util.array.ArrayFactory; @@ -68,8 +69,8 @@ public PropertyDefinition(@NotNull final EditablePropertyType propertyType, @Not } public PropertyDefinition(@NotNull final EditablePropertyType propertyType, @NotNull final String name, - @NotNull final String id, @Nullable final Object defaultValue, - @NotNull final Array options) { + @NotNull final String id, @Nullable final Object defaultValue, + @NotNull final Array options) { this.propertyType = propertyType; this.name = name; this.id = id; @@ -94,38 +95,39 @@ public PropertyDefinition(@NotNull final EditablePropertyType propertyType, @Not /** * @return the type of the property. */ - @NotNull - public EditablePropertyType getPropertyType() { + @FromAnyThread + public @NotNull EditablePropertyType getPropertyType() { return propertyType; } /** * @return the name of the property. */ - @Nullable - public Object getDefaultValue() { + @FromAnyThread + public @Nullable Object getDefaultValue() { return defaultValue; } /** * @return the id of the property. */ - @NotNull - public String getId() { + @FromAnyThread + public @NotNull String getId() { return id; } /** * @return the default value of the property. */ - @NotNull - public String getName() { + @FromAnyThread + public @NotNull String getName() { return name; } /** * @return the max value. */ + @FromAnyThread public float getMax() { return max; } @@ -133,6 +135,7 @@ public float getMax() { /** * @return the min value. */ + @FromAnyThread public float getMin() { return min; } @@ -140,8 +143,8 @@ public float getMin() { /** * @return the options. */ - @NotNull - public Array getOptions() { + @FromAnyThread + public @NotNull Array getOptions() { return options; } diff --git a/src/main/java/com/ss/editor/plugin/api/property/control/AssetResourcePropertyEditorControl.java b/src/main/java/com/ss/editor/plugin/api/property/control/AssetResourcePropertyEditorControl.java index bf7af3e9..f57ad9bd 100644 --- a/src/main/java/com/ss/editor/plugin/api/property/control/AssetResourcePropertyEditorControl.java +++ b/src/main/java/com/ss/editor/plugin/api/property/control/AssetResourcePropertyEditorControl.java @@ -1,5 +1,7 @@ package com.ss.editor.plugin.api.property.control; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.editor.ui.util.UIUtils; import com.ss.rlib.util.VarTable; @@ -34,8 +36,8 @@ protected AssetResourcePropertyEditorControl(@NotNull final VarTable vars, * * @return the action tester. */ - @NotNull - protected Predicate> getActionTester() { + @FromAnyThread + protected @NotNull Predicate> getActionTester() { return DEFAULT_ACTION_TESTER; } @@ -44,12 +46,13 @@ protected Predicate> getActionTester() { * * @return the list with available extensions. */ - @NotNull - protected Array getExtensions() { + @FromAnyThread + protected @NotNull Array getExtensions() { return DEFAULT_EXTENSIONS; } @Override + @FXThread protected void processSelect() { super.processSelect(); UIUtils.openFileAssetDialog(this::processSelect, getExtensions(), getActionTester()); @@ -60,6 +63,7 @@ protected void processSelect() { * * @param file the selected file. */ + @FXThread protected void processSelect(@NotNull final Path file) { change(); reload(); diff --git a/src/main/java/com/ss/editor/plugin/api/property/control/AwtFontPropertyEditorControl.java b/src/main/java/com/ss/editor/plugin/api/property/control/AwtFontPropertyEditorControl.java index a9e0a947..a30e9b9f 100644 --- a/src/main/java/com/ss/editor/plugin/api/property/control/AwtFontPropertyEditorControl.java +++ b/src/main/java/com/ss/editor/plugin/api/property/control/AwtFontPropertyEditorControl.java @@ -1,6 +1,7 @@ package com.ss.editor.plugin.api.property.control; import static com.ss.rlib.util.ObjectUtils.notNull; +import com.ss.editor.annotation.FXThread; import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.editor.ui.css.CSSClasses; import com.ss.rlib.ui.util.FXUtils; @@ -34,12 +35,12 @@ public class AwtFontPropertyEditorControl extends PropertyEditorControl { private static final StringConverter STRING_CONVERTER = new StringConverter() { @Override - public String toString(@Nullable final Font font) { + public @NotNull String toString(@Nullable final Font font) { return font == null ? StringUtils.EMPTY : font.getFontName(); } @Override - public Font fromString(@NotNull final String fontName) { + public @Nullable Font fromString(@NotNull final String fontName) { return Arrays.stream(FONTS) .filter(font -> font.getFontName().equals(fontName)) .findAny().orElse(null); @@ -58,19 +59,24 @@ protected AwtFontPropertyEditorControl(@NotNull final VarTable vars, @NotNull fi } @Override + @FXThread protected void createComponents() { super.createComponents(); comboBox = new ComboBox<>(); - comboBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> change()); comboBox.prefWidthProperty().bind(widthProperty().multiply(DEFAULT_FIELD_W_PERCENT)); comboBox.getItems().addAll(FONTS); comboBox.setVisibleRowCount(20); comboBox.setConverter(STRING_CONVERTER); comboBox.setEditable(true); - new AutoCompletionTextFieldBinding<>(comboBox.getEditor(), - SuggestionProvider.create(comboBox.getItems()), STRING_CONVERTER); + final SingleSelectionModel selectionModel = comboBox.getSelectionModel(); + selectionModel.selectedItemProperty().addListener((observable, oldValue, newValue) -> change()); + + final AutoCompletionTextFieldBinding binding = + new AutoCompletionTextFieldBinding<>(comboBox.getEditor(), + SuggestionProvider.create(comboBox.getItems()), STRING_CONVERTER); + binding.setOnAutoCompleted(event -> selectionModel.select(event.getCompletion())); FXUtils.addClassesTo(comboBox.getEditor(), CSSClasses.TRANSPARENT_TEXT_FIELD, CSSClasses.TEXT_FIELD_IN_COMBO_BOX); FXUtils.addClassTo(comboBox, CSSClasses.ABSTRACT_PARAM_CONTROL_COMBO_BOX); @@ -80,12 +86,13 @@ protected void createComponents() { /** * @return The list of available options of the string value. */ - @NotNull - private ComboBox getComboBox() { + @FXThread + private @NotNull ComboBox getComboBox() { return notNull(comboBox); } @Override + @FXThread protected void reload() { super.reload(); final Font value = getPropertyValue(); @@ -94,6 +101,7 @@ protected void reload() { } @Override + @FXThread protected void changeImpl() { final ComboBox comboBox = getComboBox(); final SingleSelectionModel selectionModel = comboBox.getSelectionModel(); diff --git a/src/main/java/com/ss/editor/plugin/api/property/control/BooleanPropertyEditorControl.java b/src/main/java/com/ss/editor/plugin/api/property/control/BooleanPropertyEditorControl.java index 1c42ee86..53d1dbf5 100644 --- a/src/main/java/com/ss/editor/plugin/api/property/control/BooleanPropertyEditorControl.java +++ b/src/main/java/com/ss/editor/plugin/api/property/control/BooleanPropertyEditorControl.java @@ -1,6 +1,7 @@ package com.ss.editor.plugin.api.property.control; import static com.ss.rlib.util.ObjectUtils.notNull; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.css.CSSClasses; import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.rlib.ui.util.FXUtils; @@ -28,6 +29,7 @@ protected BooleanPropertyEditorControl(@NotNull final VarTable vars, @NotNull fi } @Override + @FXThread protected void createComponents() { super.createComponents(); @@ -39,12 +41,16 @@ protected void createComponents() { FXUtils.addClassTo(checkBox, CSSClasses.ABSTRACT_PARAM_CONTROL_CHECK_BOX); } - @NotNull - private CheckBox getCheckBox() { + /** + * @return the CheckBox with current value. + */ + @FXThread + private @NotNull CheckBox getCheckBox() { return notNull(checkBox); } @Override + @FXThread protected void reload() { super.reload(); final Boolean value = getPropertyValue(); @@ -52,6 +58,7 @@ protected void reload() { } @Override + @FXThread protected void changeImpl() { setPropertyValue(getCheckBox().isSelected()); super.changeImpl(); diff --git a/src/main/java/com/ss/editor/plugin/api/property/control/ColorPropertyEditorControl.java b/src/main/java/com/ss/editor/plugin/api/property/control/ColorPropertyEditorControl.java index 1ed70974..cf023799 100644 --- a/src/main/java/com/ss/editor/plugin/api/property/control/ColorPropertyEditorControl.java +++ b/src/main/java/com/ss/editor/plugin/api/property/control/ColorPropertyEditorControl.java @@ -2,6 +2,7 @@ import static com.ss.rlib.util.ObjectUtils.notNull; import com.jme3.math.ColorRGBA; +import com.ss.editor.annotation.FXThread; import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.editor.ui.css.CSSClasses; import com.ss.editor.ui.util.UIUtils; @@ -30,6 +31,7 @@ protected ColorPropertyEditorControl(@NotNull final VarTable vars, @NotNull fina } @Override + @FXThread protected void createComponents() { super.createComponents(); @@ -44,12 +46,13 @@ protected void createComponents() { /** * @return the color picker. */ - @NotNull - private ColorPicker getColorPicker() { + @FXThread + private @NotNull ColorPicker getColorPicker() { return notNull(colorPicker); } @Override + @FXThread protected void reload() { super.reload(); final ColorPicker colorPicker = getColorPicker(); @@ -57,6 +60,7 @@ protected void reload() { } @Override + @FXThread protected void changeImpl() { final ColorPicker colorPicker = getColorPicker(); setPropertyValue(UIUtils.from(colorPicker.getValue())); diff --git a/src/main/java/com/ss/editor/plugin/api/property/control/EnumPropertyEditorControl.java b/src/main/java/com/ss/editor/plugin/api/property/control/EnumPropertyEditorControl.java index 27ca9537..7045a3bb 100644 --- a/src/main/java/com/ss/editor/plugin/api/property/control/EnumPropertyEditorControl.java +++ b/src/main/java/com/ss/editor/plugin/api/property/control/EnumPropertyEditorControl.java @@ -2,6 +2,7 @@ import static com.ss.rlib.util.ClassUtils.unsafeCast; import static com.ss.rlib.util.ObjectUtils.notNull; +import com.ss.editor.annotation.FXThread; import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.editor.ui.css.CSSClasses; import com.ss.rlib.ui.util.FXUtils; @@ -36,6 +37,7 @@ protected EnumPropertyEditorControl(@NotNull final VarTable vars, @NotNull final } @Override + @FXThread protected void createComponents() { super.createComponents(); @@ -51,12 +53,13 @@ protected void createComponents() { /** * @return the list of available options of the {@link Enum} value. */ - @NotNull - private ComboBox getEnumComboBox() { + @FXThread + private @NotNull ComboBox getEnumComboBox() { return notNull(enumComboBox); } @Override + @FXThread protected void reload() { super.reload(); final T value = getPropertyValue(); @@ -65,6 +68,7 @@ protected void reload() { } @Override + @FXThread protected void changeImpl() { final ComboBox enumComboBox = getEnumComboBox(); final SingleSelectionModel selectionModel = enumComboBox.getSelectionModel(); diff --git a/src/main/java/com/ss/editor/plugin/api/property/control/FloatPropertyEditorControl.java b/src/main/java/com/ss/editor/plugin/api/property/control/FloatPropertyEditorControl.java index 935d3aef..1f101a02 100644 --- a/src/main/java/com/ss/editor/plugin/api/property/control/FloatPropertyEditorControl.java +++ b/src/main/java/com/ss/editor/plugin/api/property/control/FloatPropertyEditorControl.java @@ -1,6 +1,7 @@ package com.ss.editor.plugin.api.property.control; import static com.ss.rlib.util.ObjectUtils.notNull; +import com.ss.editor.annotation.FXThread; import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.editor.ui.css.CSSClasses; import com.ss.rlib.ui.control.input.FloatTextField; @@ -28,6 +29,7 @@ protected FloatPropertyEditorControl(@NotNull final VarTable vars, @NotNull fina } @Override + @FXThread protected void createComponents() { super.createComponents(); @@ -39,8 +41,11 @@ protected void createComponents() { FXUtils.addToPane(valueField, this); } - @NotNull - private FloatTextField getValueField() { + /** + * @return the value field. + */ + @FXThread + private @NotNull FloatTextField getValueField() { return notNull(valueField); } @@ -50,12 +55,14 @@ private FloatTextField getValueField() { * @param min the min value. * @param max the max value. */ + @FXThread public void setMinMax(final float min, final float max) { if (Float.isNaN(min) || Float.isNaN(max)) return; getValueField().setMinMax(min, max); } @Override + @FXThread protected void reload() { super.reload(); final Float value = getPropertyValue(); @@ -63,6 +70,7 @@ protected void reload() { } @Override + @FXThread protected void changeImpl() { setPropertyValue(getValueField().getValue()); super.changeImpl(); diff --git a/src/main/java/com/ss/editor/plugin/api/property/control/GeometryAssetResourcePropertyControl.java b/src/main/java/com/ss/editor/plugin/api/property/control/GeometryAssetResourcePropertyControl.java index 55113cd6..d881d979 100644 --- a/src/main/java/com/ss/editor/plugin/api/property/control/GeometryAssetResourcePropertyControl.java +++ b/src/main/java/com/ss/editor/plugin/api/property/control/GeometryAssetResourcePropertyControl.java @@ -4,6 +4,7 @@ import com.jme3.asset.ModelKey; import com.jme3.scene.Geometry; import com.jme3.scene.Spatial; +import com.ss.editor.annotation.FXThread; import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.editor.util.NodeUtils; import com.ss.rlib.util.VarTable; @@ -23,9 +24,9 @@ public GeometryAssetResourcePropertyControl(@NotNull final VarTable vars, super(vars, definition, validationCallback); } - @Nullable @Override - protected Geometry findResource(@NotNull final AssetManager assetManager, @NotNull final ModelKey modelKey) { + @FXThread + protected @Nullable Geometry findResource(@NotNull final AssetManager assetManager, @NotNull final ModelKey modelKey) { final Spatial spatial = assetManager.loadModel(modelKey); return NodeUtils.findGeometry(spatial); } diff --git a/src/main/java/com/ss/editor/plugin/api/property/control/IntegerPropertyEditorControl.java b/src/main/java/com/ss/editor/plugin/api/property/control/IntegerPropertyEditorControl.java index 400103c9..0773c49d 100644 --- a/src/main/java/com/ss/editor/plugin/api/property/control/IntegerPropertyEditorControl.java +++ b/src/main/java/com/ss/editor/plugin/api/property/control/IntegerPropertyEditorControl.java @@ -1,6 +1,7 @@ package com.ss.editor.plugin.api.property.control; import static com.ss.rlib.util.ObjectUtils.notNull; +import com.ss.editor.annotation.FXThread; import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.editor.ui.css.CSSClasses; import com.ss.rlib.ui.control.input.IntegerTextField; @@ -28,6 +29,7 @@ protected IntegerPropertyEditorControl(@NotNull final VarTable vars, @NotNull fi } @Override + @FXThread protected void createComponents() { super.createComponents(); @@ -45,17 +47,22 @@ protected void createComponents() { * @param min the min value. * @param max the max value. */ + @FXThread public void setMinMax(final float min, final float max) { if (Float.isNaN(min) || Float.isNaN(max)) return; getValueField().setMinMax((int) min, (int) max); } - @NotNull - private IntegerTextField getValueField() { + /** + * @return the value field. + */ + @FXThread + private @NotNull IntegerTextField getValueField() { return notNull(valueField); } @Override + @FXThread protected void reload() { super.reload(); final Integer value = getPropertyValue(); @@ -63,6 +70,7 @@ protected void reload() { } @Override + @FXThread protected void changeImpl() { setPropertyValue(getValueField().getValue()); super.changeImpl(); diff --git a/src/main/java/com/ss/editor/plugin/api/property/control/PropertyEditorControl.java b/src/main/java/com/ss/editor/plugin/api/property/control/PropertyEditorControl.java index a0e69beb..fecd8771 100644 --- a/src/main/java/com/ss/editor/plugin/api/property/control/PropertyEditorControl.java +++ b/src/main/java/com/ss/editor/plugin/api/property/control/PropertyEditorControl.java @@ -1,6 +1,8 @@ package com.ss.editor.plugin.api.property.control; import com.ss.editor.Editor; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.editor.ui.css.CSSClasses; import com.ss.editor.ui.dialog.AbstractSimpleEditorDialog; @@ -94,18 +96,22 @@ protected PropertyEditorControl(@NotNull final VarTable vars, @NotNull final Pro FXUtils.addClassTo(this, CSSClasses.ABSTRACT_PARAM_EDITOR_CONTROL); } + @FXThread protected void reload() { } + @FXThread protected void change() { if (isIgnoreListener()) return; changeImpl(); } + @FXThread protected void changeImpl() { validationCallback.run(); } + @FXThread protected void createComponents() { setAlignment(Pos.CENTER_RIGHT); @@ -121,8 +127,8 @@ protected void createComponents() { * * @return the name of the property. */ - @NotNull - protected String getName() { + @FromAnyThread + protected @NotNull String getName() { return name; } @@ -131,8 +137,8 @@ protected String getName() { * * @return the current property value. */ - @Nullable - protected T getPropertyValue() { + @FromAnyThread + protected @Nullable T getPropertyValue() { if (!vars.has(id)) return null; return vars.get(id); } @@ -142,7 +148,7 @@ protected T getPropertyValue() { * * @param propertyValue the new current property value. */ - @Nullable + @FXThread protected void setPropertyValue(@Nullable final T propertyValue) { if (propertyValue == null) { vars.clear(id); @@ -156,6 +162,7 @@ protected void setPropertyValue(@Nullable final T propertyValue) { * * @param ignoreListener the flag for ignoring listeners. */ + @FXThread protected void setIgnoreListener(final boolean ignoreListener) { this.ignoreListener = ignoreListener; } @@ -165,6 +172,7 @@ protected void setIgnoreListener(final boolean ignoreListener) { * * @return true if need to ignore listeners. */ + @FXThread protected boolean isIgnoreListener() { return ignoreListener; } diff --git a/src/main/java/com/ss/editor/plugin/api/property/control/PropertyEditorControlFactory.java b/src/main/java/com/ss/editor/plugin/api/property/control/PropertyEditorControlFactory.java index 9e97aae0..1af84a34 100644 --- a/src/main/java/com/ss/editor/plugin/api/property/control/PropertyEditorControlFactory.java +++ b/src/main/java/com/ss/editor/plugin/api/property/control/PropertyEditorControlFactory.java @@ -1,5 +1,6 @@ package com.ss.editor.plugin.api.property.control; +import com.ss.editor.annotation.FXThread; import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.rlib.util.VarTable; import org.jetbrains.annotations.NotNull; @@ -11,10 +12,18 @@ */ public class PropertyEditorControlFactory { - @NotNull - public static PropertyEditorControl build(@NotNull final VarTable vars, - @NotNull final PropertyDefinition definition, - @NotNull final Runnable validation) { + /** + * Build the property control for the property definition. + * + * @param vars the variables table. + * @param definition the definition. + * @param validation the validator. + * @return the new property control. + */ + @FXThread + public static @NotNull PropertyEditorControl build(@NotNull final VarTable vars, + @NotNull final PropertyDefinition definition, + @NotNull final Runnable validation) { switch (definition.getPropertyType()) { case FLOAT: { @@ -22,20 +31,29 @@ public static PropertyEditorControl build(@NotNull final VarTable vars, control.setMinMax(definition.getMin(), definition.getMax()); return control; } - case COLOR: return new ColorPropertyEditorControl(vars, definition, validation); - case BOOLEAN: return new BooleanPropertyEditorControl(vars, definition, validation); + case COLOR: + return new ColorPropertyEditorControl(vars, definition, validation); + case BOOLEAN: + return new BooleanPropertyEditorControl(vars, definition, validation); case INTEGER: { final IntegerPropertyEditorControl control = new IntegerPropertyEditorControl(vars, definition, validation); control.setMinMax(definition.getMin(), definition.getMax()); return control; } - case VECTOR_3F: return new Vector3fPropertyEditorControl(vars, definition, validation); - case ENUM: return new EnumPropertyEditorControl<>(vars, definition, validation); - case STRING: return new StringPropertyEditorControl(vars, definition, validation); - case GEOMETRY_FROM_ASSET_FOLDER: return new GeometryAssetResourcePropertyControl(vars, definition, validation); - case STRING_FROM_LIST: return new StringFromListPropertyEditorControl(vars, definition, validation, definition.getOptions()); - case AWT_FONT: return new AwtFontPropertyEditorControl(vars, definition, validation); - default: throw new IllegalArgumentException("Unknown the type " + definition.getPropertyType()); + case VECTOR_3F: + return new Vector3fPropertyEditorControl(vars, definition, validation); + case ENUM: + return new EnumPropertyEditorControl<>(vars, definition, validation); + case STRING: + return new StringPropertyEditorControl(vars, definition, validation); + case GEOMETRY_FROM_ASSET_FOLDER: + return new GeometryAssetResourcePropertyControl(vars, definition, validation); + case STRING_FROM_LIST: + return new StringFromListPropertyEditorControl(vars, definition, validation, definition.getOptions()); + case AWT_FONT: + return new AwtFontPropertyEditorControl(vars, definition, validation); + default: + throw new IllegalArgumentException("Unknown the type " + definition.getPropertyType()); } } } diff --git a/src/main/java/com/ss/editor/plugin/api/property/control/ResourcePropertyEditorControl.java b/src/main/java/com/ss/editor/plugin/api/property/control/ResourcePropertyEditorControl.java index cdcb2e42..604124da 100644 --- a/src/main/java/com/ss/editor/plugin/api/property/control/ResourcePropertyEditorControl.java +++ b/src/main/java/com/ss/editor/plugin/api/property/control/ResourcePropertyEditorControl.java @@ -3,6 +3,7 @@ import static com.ss.rlib.util.ClassUtils.unsafeCast; import static com.ss.rlib.util.ObjectUtils.notNull; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.css.CSSClasses; import com.ss.editor.plugin.api.property.PropertyDefinition; @@ -53,6 +54,7 @@ protected ResourcePropertyEditorControl(@NotNull final VarTable vars, @NotNull f } @Override + @FXThread protected void createComponents() { super.createComponents(); @@ -78,6 +80,7 @@ protected void createComponents() { /** * Process select a resource. */ + @FXThread protected void processSelect() { } @@ -85,12 +88,14 @@ protected void processSelect() { /** * Handle grad exiting. */ + @FXThread private void dragExited(@NotNull final DragEvent dragEvent) { } /** * Handle dropped files to editor. */ + @FXThread private void dragDropped(@NotNull final DragEvent dragEvent) { final Dragboard dragboard = dragEvent.getDragboard(); @@ -111,12 +116,14 @@ private void dragDropped(@NotNull final DragEvent dragEvent) { * * @param file the dropped file. */ + @FXThread protected void handleFile(@NotNull final File file) { } /** * Handle drag over. */ + @FXThread private void dragOver(@NotNull final DragEvent dragEvent) { final Dragboard dragboard = dragEvent.getDragboard(); @@ -136,6 +143,7 @@ private void dragOver(@NotNull final DragEvent dragEvent) { dragEvent.consume(); } + @FXThread protected boolean canAccept(@NotNull final File file) { return false; } @@ -143,8 +151,8 @@ protected boolean canAccept(@NotNull final File file) { /** * @return the label with name of the resource. */ - @NotNull - protected Label getResourceLabel() { + @FXThread + protected @NotNull Label getResourceLabel() { return notNull(resourceLabel); } } diff --git a/src/main/java/com/ss/editor/plugin/api/property/control/SpatialAssetResourcePropertyControl.java b/src/main/java/com/ss/editor/plugin/api/property/control/SpatialAssetResourcePropertyControl.java index 7ed8ff37..19da7297 100644 --- a/src/main/java/com/ss/editor/plugin/api/property/control/SpatialAssetResourcePropertyControl.java +++ b/src/main/java/com/ss/editor/plugin/api/property/control/SpatialAssetResourcePropertyControl.java @@ -10,6 +10,8 @@ import com.jme3.asset.ModelKey; import com.jme3.scene.Spatial; import com.ss.editor.FileExtensions; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.rlib.util.VarTable; import com.ss.rlib.util.array.Array; @@ -40,13 +42,14 @@ public SpatialAssetResourcePropertyControl(@NotNull final VarTable vars, super(vars, definition, validationCallback); } - @NotNull @Override - protected Array getExtensions() { + @FromAnyThread + protected @NotNull Array getExtensions() { return EXTENSIONS; } @Override + @FXThread protected void processSelect(@NotNull final Path file) { final AssetManager assetManager = EDITOR.getAssetManager(); @@ -67,12 +70,13 @@ protected void processSelect(@NotNull final Path file) { * @param modelKey the model key. * @return the target resource. */ - @Nullable - protected T findResource(@NotNull final AssetManager assetManager, @NotNull final ModelKey modelKey) { + @FXThread + protected @Nullable T findResource(@NotNull final AssetManager assetManager, @NotNull final ModelKey modelKey) { return unsafeCast(assetManager.loadModel(modelKey)); } @Override + @FXThread protected void reload() { final T model = getPropertyValue(); diff --git a/src/main/java/com/ss/editor/plugin/api/property/control/StringFromListPropertyEditorControl.java b/src/main/java/com/ss/editor/plugin/api/property/control/StringFromListPropertyEditorControl.java index e423cd70..63902dec 100644 --- a/src/main/java/com/ss/editor/plugin/api/property/control/StringFromListPropertyEditorControl.java +++ b/src/main/java/com/ss/editor/plugin/api/property/control/StringFromListPropertyEditorControl.java @@ -1,6 +1,7 @@ package com.ss.editor.plugin.api.property.control; import static com.ss.rlib.util.ObjectUtils.notNull; +import com.ss.editor.annotation.FXThread; import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.editor.ui.css.CSSClasses; import com.ss.rlib.ui.util.FXUtils; @@ -8,6 +9,8 @@ import com.ss.rlib.util.array.Array; import javafx.scene.control.ComboBox; import javafx.scene.control.SingleSelectionModel; +import javafx.scene.control.TextField; +import org.controlsfx.control.textfield.AutoCompletionBinding; import org.controlsfx.control.textfield.TextFields; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -35,9 +38,15 @@ protected StringFromListPropertyEditorControl(@NotNull final VarTable vars, @Not if (options.size() > comboBox.getVisibleRowCount()) { setIgnoreListener(true); try { + comboBox.setEditable(true); - TextFields.bindAutoCompletion(comboBox.getEditor(), comboBox.getItems()); - FXUtils.addClassesTo(comboBox.getEditor(), CSSClasses.TRANSPARENT_TEXT_FIELD, CSSClasses.TEXT_FIELD_IN_COMBO_BOX); + + final TextField editor = comboBox.getEditor(); + final SingleSelectionModel selectionModel = comboBox.getSelectionModel(); + final AutoCompletionBinding binding = TextFields.bindAutoCompletion(editor, comboBox.getItems()); + binding.setOnAutoCompleted(event -> selectionModel.select(event.getCompletion())); + + FXUtils.addClassesTo(editor, CSSClasses.TRANSPARENT_TEXT_FIELD, CSSClasses.TEXT_FIELD_IN_COMBO_BOX); reload(); } finally { setIgnoreListener(false); @@ -46,6 +55,7 @@ protected StringFromListPropertyEditorControl(@NotNull final VarTable vars, @Not } @Override + @FXThread protected void createComponents() { super.createComponents(); @@ -61,12 +71,13 @@ protected void createComponents() { /** * @return The list of available options of the string value. */ - @NotNull - private ComboBox getComboBox() { + @FXThread + private @NotNull ComboBox getComboBox() { return notNull(comboBox); } @Override + @FXThread protected void reload() { super.reload(); final String value = getPropertyValue(); @@ -75,6 +86,7 @@ protected void reload() { } @Override + @FXThread protected void changeImpl() { final ComboBox comboBox = getComboBox(); final SingleSelectionModel selectionModel = comboBox.getSelectionModel(); diff --git a/src/main/java/com/ss/editor/plugin/api/property/control/StringPropertyEditorControl.java b/src/main/java/com/ss/editor/plugin/api/property/control/StringPropertyEditorControl.java index 8f9317d5..9fbb4de1 100644 --- a/src/main/java/com/ss/editor/plugin/api/property/control/StringPropertyEditorControl.java +++ b/src/main/java/com/ss/editor/plugin/api/property/control/StringPropertyEditorControl.java @@ -1,6 +1,7 @@ package com.ss.editor.plugin.api.property.control; import static com.ss.rlib.util.ObjectUtils.notNull; +import com.ss.editor.annotation.FXThread; import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.editor.ui.css.CSSClasses; import com.ss.rlib.ui.util.FXUtils; @@ -28,6 +29,7 @@ protected StringPropertyEditorControl(@NotNull final VarTable vars, @NotNull fin } @Override + @FXThread protected void createComponents() { super.createComponents(); @@ -39,12 +41,16 @@ protected void createComponents() { FXUtils.addToPane(valueField, this); } - @NotNull - private TextField getValueField() { + /** + * @return the value field. + */ + @FXThread + private @NotNull TextField getValueField() { return notNull(valueField); } @Override + @FXThread protected void reload() { super.reload(); final String value = getPropertyValue(); @@ -52,6 +58,7 @@ protected void reload() { } @Override + @FXThread protected void changeImpl() { setPropertyValue(getValueField().getText()); super.changeImpl(); diff --git a/src/main/java/com/ss/editor/plugin/api/property/control/Vector3fPropertyEditorControl.java b/src/main/java/com/ss/editor/plugin/api/property/control/Vector3fPropertyEditorControl.java index b07f803e..17364ada 100644 --- a/src/main/java/com/ss/editor/plugin/api/property/control/Vector3fPropertyEditorControl.java +++ b/src/main/java/com/ss/editor/plugin/api/property/control/Vector3fPropertyEditorControl.java @@ -1,6 +1,7 @@ package com.ss.editor.plugin.api.property.control; import com.jme3.math.Vector3f; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.css.CSSClasses; import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.editor.ui.util.UIUtils; @@ -42,6 +43,7 @@ protected Vector3fPropertyEditorControl(@NotNull final VarTable vars, @NotNull f } @Override + @FXThread protected void createComponents() { super.createComponents(); @@ -75,28 +77,29 @@ protected void createComponents() { /** * @return the field X. */ - @NotNull - private FloatTextField getXField() { + @FXThread + private @NotNull FloatTextField getXField() { return xField; } /** * @return the field Y. */ - @NotNull - private FloatTextField getYField() { + @FXThread + private @NotNull FloatTextField getYField() { return yField; } /** * @return the field Z. */ - @NotNull - private FloatTextField getZField() { + @FXThread + private @NotNull FloatTextField getZField() { return zField; } @Override + @FXThread protected void reload() { super.reload(); @@ -113,6 +116,7 @@ protected void reload() { } @Override + @FXThread protected void changeImpl() { final FloatTextField xField = getXField(); final FloatTextField yField = getYField(); diff --git a/src/main/java/com/ss/editor/ui/component/creator/impl/AbstractFileCreator.java b/src/main/java/com/ss/editor/ui/component/creator/impl/AbstractFileCreator.java index d5680fe6..f4ceba14 100644 --- a/src/main/java/com/ss/editor/ui/component/creator/impl/AbstractFileCreator.java +++ b/src/main/java/com/ss/editor/ui/component/creator/impl/AbstractFileCreator.java @@ -251,6 +251,7 @@ protected void processOk() { * Write created data to the created file. * * @param resultFile the result file. + * @throws IOException if was some problem with writing to the result file. */ @BackgroundThread protected void writeData(@NotNull final Path resultFile) throws IOException { diff --git a/src/main/java/com/ss/editor/ui/component/editor/impl/AbstractFileEditor.java b/src/main/java/com/ss/editor/ui/component/editor/impl/AbstractFileEditor.java index 5b8a18ba..115d685e 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/impl/AbstractFileEditor.java +++ b/src/main/java/com/ss/editor/ui/component/editor/impl/AbstractFileEditor.java @@ -388,6 +388,7 @@ public void save(@Nullable final Consumer<@NotNull FileEditor> callback) { * Save new changes. * * @param toStore the file to store. + * @throws IOException if was some problem with writing to the to store file. */ @BackgroundThread protected void doSave(@NotNull final Path toStore) throws IOException { diff --git a/src/main/java/com/ss/editor/ui/component/editor/impl/material/MaterialFileEditor.java b/src/main/java/com/ss/editor/ui/component/editor/impl/material/MaterialFileEditor.java index 7d435c95..33fedc2b 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/impl/material/MaterialFileEditor.java +++ b/src/main/java/com/ss/editor/ui/component/editor/impl/material/MaterialFileEditor.java @@ -203,15 +203,15 @@ private void applyTexture(@NotNull final MaterialFileEditor editor, @NotNull fin } @Override - protected void dragDropped(@NotNull final DragEvent dragEvent) { - super.dragDropped(dragEvent); + protected void handleDragDroppedEvent(@NotNull final DragEvent dragEvent) { + super.handleDragDroppedEvent(dragEvent); UIUtils.handleDroppedFile(dragEvent, FileExtensions.TEXTURE_EXTENSIONS, this, dragEvent, this::applyTexture); } @Override - protected void dragOver(@NotNull final DragEvent dragEvent) { - super.dragOver(dragEvent); + protected void handleDragOverEvent(@NotNull final DragEvent dragEvent) { + super.handleDragOverEvent(dragEvent); UIUtils.acceptIfHasFile(dragEvent, FileExtensions.TEXTURE_EXTENSIONS); } diff --git a/src/main/java/com/ss/editor/ui/component/editor/impl/scene/AbstractSceneFileEditor.java b/src/main/java/com/ss/editor/ui/component/editor/impl/scene/AbstractSceneFileEditor.java index 1b4f93ce..20226bb1 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/impl/scene/AbstractSceneFileEditor.java +++ b/src/main/java/com/ss/editor/ui/component/editor/impl/scene/AbstractSceneFileEditor.java @@ -1041,8 +1041,8 @@ protected void processChangeTool(@Nullable final Number oldValue, @NotNull final } @Override - protected void dragDropped(@NotNull final DragEvent dragEvent) { - super.dragDropped(dragEvent); + protected void handleDragDroppedEvent(@NotNull final DragEvent dragEvent) { + super.handleDragDroppedEvent(dragEvent); UIUtils.handleDroppedFile(dragEvent, FileExtensions.JME_OBJECT, this, dragEvent, AbstractSceneFileEditor::addNewModel); @@ -1052,8 +1052,8 @@ protected void dragDropped(@NotNull final DragEvent dragEvent) { } @Override - protected void dragOver(@NotNull final DragEvent dragEvent) { - super.dragOver(dragEvent); + protected void handleDragOverEvent(@NotNull final DragEvent dragEvent) { + super.handleDragOverEvent(dragEvent); UIUtils.acceptIfHasFile(dragEvent, ACCEPTED_FILES); } diff --git a/src/main/java/com/ss/editor/ui/control/model/property/ModelPropertyEditor.java b/src/main/java/com/ss/editor/ui/control/model/property/ModelPropertyEditor.java index f12d6497..8ceb8ba7 100644 --- a/src/main/java/com/ss/editor/ui/control/model/property/ModelPropertyEditor.java +++ b/src/main/java/com/ss/editor/ui/control/model/property/ModelPropertyEditor.java @@ -4,6 +4,7 @@ import com.jme3.material.Material; import com.jme3.scene.AssetLinkNode; import com.jme3.scene.Spatial; +import com.ss.editor.annotation.FXThread; import com.ss.editor.model.node.particles.Toneg0dParticleInfluencers; import com.ss.editor.model.undo.editor.ModelChangeConsumer; import com.ss.editor.ui.control.property.PropertyEditor; @@ -28,6 +29,7 @@ public ModelPropertyEditor(@NotNull final ModelChangeConsumer changeConsumer) { super(changeConsumer); } + @FXThread protected boolean isNeedUpdate(@Nullable final Object object) { final Object currentObject = getCurrentObject(); @@ -40,6 +42,7 @@ protected boolean isNeedUpdate(@Nullable final Object object) { return super.isNeedUpdate(object); } + @FXThread @Override protected boolean canEdit(@NotNull final Object object, @Nullable final Object parent) { diff --git a/src/main/java/com/ss/editor/ui/control/model/property/builder/impl/MaterialPropertyBuilder.java b/src/main/java/com/ss/editor/ui/control/model/property/builder/impl/MaterialPropertyBuilder.java index b6f32bd9..eba118e0 100644 --- a/src/main/java/com/ss/editor/ui/control/model/property/builder/impl/MaterialPropertyBuilder.java +++ b/src/main/java/com/ss/editor/ui/control/model/property/builder/impl/MaterialPropertyBuilder.java @@ -96,7 +96,7 @@ protected MaterialPropertyBuilder() { } protected void applyParam(@NotNull final MatParam param, @NotNull final Material object, - @Nullable final Object newValue) { + @Nullable final Object newValue) { if (newValue == null) { object.clearParam(param.getName()); } else { diff --git a/src/main/java/com/ss/editor/ui/control/model/property/control/DirectionLightElementModelPropertyControl.java b/src/main/java/com/ss/editor/ui/control/model/property/control/DirectionLightElementModelPropertyControl.java index 897b0de4..0e940c33 100644 --- a/src/main/java/com/ss/editor/ui/control/model/property/control/DirectionLightElementModelPropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/model/property/control/DirectionLightElementModelPropertyControl.java @@ -1,6 +1,7 @@ package com.ss.editor.ui.control.model.property.control; import com.jme3.light.DirectionalLight; +import com.ss.editor.annotation.FXThread; import com.ss.editor.model.undo.editor.ModelChangeConsumer; import com.ss.editor.ui.control.model.tree.dialog.LightSelectorDialog; import com.ss.editor.ui.control.model.tree.dialog.NodeSelectorDialog; @@ -28,6 +29,7 @@ protected NodeSelectorDialog createNodeSelectorDialog() { return new LightSelectorDialog<>(changeConsumer.getCurrentModel(), type, this::processAdd); } + @FXThread @Override protected void reload() { diff --git a/src/main/java/com/ss/editor/ui/control/model/property/control/LayerModelPropertyControl.java b/src/main/java/com/ss/editor/ui/control/model/property/control/LayerModelPropertyControl.java index ffebc187..680ac403 100644 --- a/src/main/java/com/ss/editor/ui/control/model/property/control/LayerModelPropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/model/property/control/LayerModelPropertyControl.java @@ -3,6 +3,8 @@ import static com.ss.rlib.util.ObjectUtils.notNull; import com.jme3.scene.Spatial; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.extension.scene.SceneLayer; import com.ss.editor.extension.scene.SceneNode; import com.ss.editor.model.undo.editor.ChangeConsumer; @@ -63,6 +65,7 @@ public LayerModelPropertyControl(@Nullable final SceneLayer layer, @NotNull fina setSyncHandler(this::getLayer); } + @FromAnyThread @Override public @NotNull SixObjectConsumer> newChangeHandler() { return (changeConsumer, object, propName, newValue, oldValue, handler) -> { @@ -84,11 +87,13 @@ private SceneLayer getLayer(@NotNull final Spatial spatial) { return sceneLayer == null ? SceneLayer.NO_LAYER : sceneLayer; } + @FromAnyThread @Override protected boolean isSingleRow() { return true; } + @FXThread @Override protected void createComponents(@NotNull final HBox container) { super.createComponents(container); @@ -119,6 +124,7 @@ private void updateLevel(@Nullable final SceneLayer layer) { return notNull(layerComboBox); } + @FXThread @Override protected void reload() { diff --git a/src/main/java/com/ss/editor/ui/control/model/property/control/NodeElementModelPropertyControl.java b/src/main/java/com/ss/editor/ui/control/model/property/control/NodeElementModelPropertyControl.java index cb3f2889..35a1518c 100644 --- a/src/main/java/com/ss/editor/ui/control/model/property/control/NodeElementModelPropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/model/property/control/NodeElementModelPropertyControl.java @@ -2,6 +2,7 @@ import com.jme3.scene.Node; import com.jme3.scene.Spatial; +import com.ss.editor.annotation.FXThread; import com.ss.editor.model.undo.editor.ModelChangeConsumer; import com.ss.editor.ui.control.model.tree.dialog.NodeSelectorDialog; import javafx.scene.control.Label; @@ -27,6 +28,7 @@ protected NodeSelectorDialog createNodeSelectorDialog() { return new NodeSelectorDialog<>(changeConsumer.getCurrentModel(), type, this::processAdd); } + @FXThread @Override protected void reload() { diff --git a/src/main/java/com/ss/editor/ui/control/model/property/control/PointLightElementModelPropertyControl.java b/src/main/java/com/ss/editor/ui/control/model/property/control/PointLightElementModelPropertyControl.java index 1388317b..cfa7534c 100644 --- a/src/main/java/com/ss/editor/ui/control/model/property/control/PointLightElementModelPropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/model/property/control/PointLightElementModelPropertyControl.java @@ -1,6 +1,7 @@ package com.ss.editor.ui.control.model.property.control; import com.jme3.light.PointLight; +import com.ss.editor.annotation.FXThread; import com.ss.editor.model.undo.editor.ModelChangeConsumer; import com.ss.editor.ui.control.model.tree.dialog.LightSelectorDialog; import com.ss.editor.ui.control.model.tree.dialog.NodeSelectorDialog; @@ -28,6 +29,7 @@ protected NodeSelectorDialog createNodeSelectorDialog() { return new LightSelectorDialog<>(changeConsumer.getCurrentModel(), type, this::processAdd); } + @FXThread @Override protected void reload() { diff --git a/src/main/java/com/ss/editor/ui/control/model/property/control/SpatialElementModelPropertyControl.java b/src/main/java/com/ss/editor/ui/control/model/property/control/SpatialElementModelPropertyControl.java index 13cadb3a..929f603b 100644 --- a/src/main/java/com/ss/editor/ui/control/model/property/control/SpatialElementModelPropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/model/property/control/SpatialElementModelPropertyControl.java @@ -1,6 +1,7 @@ package com.ss.editor.ui.control.model.property.control; import com.jme3.scene.Spatial; +import com.ss.editor.annotation.FXThread; import com.ss.editor.model.undo.editor.ModelChangeConsumer; import com.ss.editor.ui.control.model.tree.dialog.NodeSelectorDialog; import com.ss.editor.ui.control.model.tree.dialog.SpatialSelectorDialog; @@ -27,6 +28,7 @@ protected NodeSelectorDialog createNodeSelectorDialog() { return new SpatialSelectorDialog<>(changeConsumer.getCurrentModel(), type, this::processAdd); } + @FXThread @Override protected void reload() { diff --git a/src/main/java/com/ss/editor/ui/control/model/property/control/particle/MaterialEmitterPropertyControl.java b/src/main/java/com/ss/editor/ui/control/model/property/control/particle/MaterialEmitterPropertyControl.java index aceffe56..693b6577 100644 --- a/src/main/java/com/ss/editor/ui/control/model/property/control/particle/MaterialEmitterPropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/model/property/control/particle/MaterialEmitterPropertyControl.java @@ -3,6 +3,7 @@ import static com.ss.editor.util.EditorUtil.getRealFile; import com.jme3.asset.AssetKey; import com.jme3.material.Material; +import com.ss.editor.annotation.FXThread; import com.ss.editor.model.undo.editor.ModelChangeConsumer; import com.ss.editor.ui.control.property.impl.MaterialPropertyControl; import com.ss.editor.ui.dialog.asset.ParticlesAssetEditorDialog; @@ -34,6 +35,7 @@ public MaterialEmitterPropertyControl(@NotNull final ParticlesMaterial element, /** * Show dialog for choosing another material. */ + @FXThread protected void processChange() { final ParticlesAssetEditorDialog dialog = new ParticlesAssetEditorDialog(this::addMaterial); dialog.setExtensionFilter(MATERIAL_EXTENSIONS); @@ -47,6 +49,7 @@ private void addMaterial(@NotNull final ParticlesMaterial particlesMaterial) { changed(particlesMaterial, getPropertyValue()); } + @FXThread @Override protected void processEdit() { @@ -67,6 +70,7 @@ protected void processEdit() { FX_EVENT_MANAGER.notify(event); } + @FXThread @Override protected void reload() { diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/action/operation/AddChildOperation.java b/src/main/java/com/ss/editor/ui/control/model/tree/action/operation/AddChildOperation.java index c79d9734..7154f3e7 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/action/operation/AddChildOperation.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/action/operation/AddChildOperation.java @@ -33,22 +33,10 @@ public class AddChildOperation extends AbstractEditorOperation> newChangeHandler() { + @FromAnyThread + public @NotNull SixObjectConsumer<@NotNull C, @NotNull D, @NotNull String, @Nullable T, @Nullable T, @NotNull BiConsumer> newChangeHandler() { return (changeConsumer, object, propName, newValue, oldValue, handler) -> { final PropertyOperation operation = new PropertyOperation<>(object, propName, newValue, oldValue); @@ -237,6 +238,7 @@ public void setApplyHandler(@NotNull final BiConsumer applyHandler) { * * @return the handler for getting actual value. */ + @FromAnyThread protected @Nullable Function getSyncHandler() { return syncHandler; } @@ -256,6 +258,7 @@ public void setSyncHandler(@Nullable final Function syncHandler) { * * @return the edit object. */ + @FromAnyThread protected @NotNull D getEditObject() { return notNull(editObject); } @@ -265,6 +268,7 @@ public void setSyncHandler(@Nullable final Function syncHandler) { * * @return true if this control has an edit object. */ + @FromAnyThread protected boolean hasEditObject() { return editObject != null; } @@ -272,6 +276,7 @@ protected boolean hasEditObject() { /** * Initializing control. */ + @FXThread protected void reload() { } @@ -303,6 +308,7 @@ public void sync() { /** * Create this control. */ + @FXThread protected void createComponents() { setAlignment(isSingleRow() ? Pos.CENTER_RIGHT : Pos.CENTER); @@ -331,6 +337,7 @@ protected void createComponents() { * * @return true if this control is single row. */ + @FromAnyThread protected boolean isSingleRow() { return false; } @@ -340,6 +347,7 @@ protected boolean isSingleRow() { * * @param container the container */ + @FXThread protected void createComponents(@NotNull final HBox container) { } @@ -348,6 +356,7 @@ protected void createComponents(@NotNull final HBox container) { * * @return the name of the property. */ + @FromAnyThread protected @NotNull String getPropertyName() { return propertyName; } @@ -357,6 +366,7 @@ protected void createComponents(@NotNull final HBox container) { * * @return the consumer of changes. */ + @FromAnyThread protected @NotNull C getChangeConsumer() { return changeConsumer; } @@ -367,6 +377,7 @@ protected void createComponents(@NotNull final HBox container) { * @param newValue the new value * @param oldValue the old value */ + @FXThread protected void changed(@Nullable final T newValue, @Nullable final T oldValue) { changeHandler.accept(getChangeConsumer(), getEditObject(), getPropertyName(), newValue, oldValue, getApplyHandler()); } @@ -386,6 +397,7 @@ protected void changed(@Nullable final T newValue, @Nullable final T oldValue) { * * @return the handler for handling new value. */ + @FromAnyThread protected @NotNull BiConsumer getApplyHandler() { return notNull(applyHandler); } @@ -395,6 +407,7 @@ protected void changed(@Nullable final T newValue, @Nullable final T oldValue) { * * @param propertyValue the value of the property. */ + @FXThread protected void setPropertyValue(@Nullable final T propertyValue) { this.propertyValue = propertyValue; } @@ -404,6 +417,7 @@ protected void setPropertyValue(@Nullable final T propertyValue) { * * @param ignoreListener the flag for ignoring listeners. */ + @FXThread protected void setIgnoreListener(final boolean ignoreListener) { this.ignoreListener = ignoreListener; } @@ -413,6 +427,7 @@ protected void setIgnoreListener(final boolean ignoreListener) { * * @return true if need to ignore listeners. */ + @FXThread protected boolean isIgnoreListener() { return ignoreListener; } diff --git a/src/main/java/com/ss/editor/ui/control/property/PropertyEditor.java b/src/main/java/com/ss/editor/ui/control/property/PropertyEditor.java index ca05205a..96d7d204 100644 --- a/src/main/java/com/ss/editor/ui/control/property/PropertyEditor.java +++ b/src/main/java/com/ss/editor/ui/control/property/PropertyEditor.java @@ -1,6 +1,7 @@ package com.ss.editor.ui.control.property; import static com.ss.rlib.util.ObjectUtils.notNull; +import com.ss.editor.annotation.FXThread; import com.ss.editor.extension.property.EditableProperty; import com.ss.editor.model.undo.editor.ChangeConsumer; import com.ss.editor.ui.FXConstants; @@ -63,6 +64,7 @@ public PropertyEditor(@NotNull final C changeConsumer) { /** * @return The container of controls. */ + @FXThread private @NotNull VBox getContainer() { return notNull(container); } @@ -70,6 +72,7 @@ public PropertyEditor(@NotNull final C changeConsumer) { /** * Create components. */ + @FXThread private void createComponents() { this.container = new VBox(); this.container.prefWidthProperty() @@ -89,6 +92,7 @@ private void createComponents() { * * @param object the object */ + @FXThread public void syncFor(@Nullable final Object object) { if (!isNeedUpdate(object)) return; @@ -106,6 +110,7 @@ public void syncFor(@Nullable final Object object) { /** * Sync all properties with controls. */ + @FXThread public void refresh() { final Object object = getCurrentObject(); @@ -128,6 +133,7 @@ public void refresh() { * @param object the object * @param parent the parent */ + @FXThread public void buildFor(@Nullable final Object object, @Nullable final Object parent) { if (getCurrentObject() == object) return; @@ -152,6 +158,7 @@ public void buildFor(@Nullable final Object object, @Nullable final Object paren * @param parent the parent. * @return true if we can edit properties of the object. */ + @FXThread protected boolean canEdit(@NotNull final Object object, @Nullable final Object parent) { return true; } @@ -162,6 +169,7 @@ protected boolean canEdit(@NotNull final Object object, @Nullable final Object p * @param object the object * @param parent the parent */ + @FXThread public void rebuildFor(@Nullable final Object object, @Nullable final Object parent) { if (getCurrentObject() != object) return; @@ -177,6 +185,7 @@ public void rebuildFor(@Nullable final Object object, @Nullable final Object par /** * Rebuild this editor. */ + @FXThread public void rebuild() { rebuildFor(getCurrentObject(), null); } @@ -187,6 +196,7 @@ public void rebuild() { * @param object the object * @return the boolean */ + @FXThread protected boolean isNeedUpdate(@Nullable final Object object) { final Object currentObject = getCurrentObject(); if (object instanceof EditableProperty) { @@ -198,6 +208,7 @@ protected boolean isNeedUpdate(@Nullable final Object object) { /** * @param currentObject the current editable object. */ + @FXThread private void setCurrentObject(@Nullable final Object currentObject) { this.currentObject = currentObject; } @@ -207,6 +218,7 @@ private void setCurrentObject(@Nullable final Object currentObject) { * * @return the current editable object. */ + @FXThread protected @Nullable Object getCurrentObject() { return currentObject; } @@ -216,6 +228,7 @@ private void setCurrentObject(@Nullable final Object currentObject) { * * @param currentParent the current parent. */ + @FXThread protected void setCurrentParent(@Nullable final Object currentParent) { this.currentParent = currentParent; } @@ -225,6 +238,7 @@ protected void setCurrentParent(@Nullable final Object currentParent) { * * @return the current parent. */ + @FXThread protected @Nullable Object getCurrentParent() { return currentParent; } diff --git a/src/main/java/com/ss/editor/ui/control/property/builder/impl/EditableObjectPropertyBuilder.java b/src/main/java/com/ss/editor/ui/control/property/builder/impl/EditableObjectPropertyBuilder.java index 428c433c..52fbc334 100644 --- a/src/main/java/com/ss/editor/ui/control/property/builder/impl/EditableObjectPropertyBuilder.java +++ b/src/main/java/com/ss/editor/ui/control/property/builder/impl/EditableObjectPropertyBuilder.java @@ -4,6 +4,7 @@ import com.jme3.math.ColorRGBA; import com.jme3.math.Vector2f; import com.jme3.math.Vector3f; +import com.jme3.math.Vector4f; import com.jme3.texture.Texture2D; import com.ss.editor.extension.property.EditableProperty; import com.ss.editor.extension.property.EditablePropertyType; @@ -80,7 +81,16 @@ protected void buildFor(@NotNull final VBox container, @NotNull final C changeCo case COLOR: { final EditableProperty property = cast(description); - final ColorRGBA color = property.getValue(); + final Object undefine = description.getValue(); + final ColorRGBA color; + + // for some cases with materials + if (undefine instanceof Vector4f) { + final Vector4f vector4f = (Vector4f) undefine; + color = new ColorRGBA(vector4f.getX(), vector4f.getY(), vector4f.getZ(), vector4f.getW()); + } else { + color = (ColorRGBA) undefine; + } final ColorPropertyControl> propertyControl = new ColorPropertyControl<>(color, property.getName(), changeConsumer); diff --git a/src/main/java/com/ss/editor/ui/control/property/impl/AudioKeyPropertyControl.java b/src/main/java/com/ss/editor/ui/control/property/impl/AudioKeyPropertyControl.java index 734eca94..43631806 100644 --- a/src/main/java/com/ss/editor/ui/control/property/impl/AudioKeyPropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/property/impl/AudioKeyPropertyControl.java @@ -7,6 +7,7 @@ import com.jme3.audio.AudioKey; import com.jme3.audio.AudioNode; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; import com.ss.editor.model.undo.editor.ChangeConsumer; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.property.PropertyControl; @@ -47,33 +48,40 @@ public class AudioKeyPropertyControl extends PropertyC public AudioKeyPropertyControl(@Nullable final AudioKey element, @NotNull final String paramName, @NotNull final C changeConsumer) { super(element, paramName, changeConsumer); - setOnDragOver(this::dragOver); - setOnDragDropped(this::dragDropped); - setOnDragExited(this::dragExited); + setOnDragOver(this::handleDragOverEvent); + setOnDragDropped(this::handleDragDroppedEvent); + setOnDragExited(this::handleDragExitedEvent); } /** - * Handle grad exiting. + * Handle grad exited events. + * + * @param dragEvent the drag exited event. */ - private void dragExited(@NotNull final DragEvent dragEvent) { + private void handleDragExitedEvent(@NotNull final DragEvent dragEvent) { } /** - * Handle dropped files to editor. + * Handle dropped event. + * + * @param dragEvent the dropped event. */ - private void dragDropped(@NotNull final DragEvent dragEvent) { + private void handleDragDroppedEvent(@NotNull final DragEvent dragEvent) { UIUtils.handleDroppedFile(dragEvent, AUDIO_EXTENSIONS, this, AudioKeyPropertyControl::addAudioData); } /** - * Handle drag over. + * Handle drag over events. + * + * @param dragEvent the drag over events. */ - private void dragOver(@NotNull final DragEvent dragEvent) { + private void handleDragOverEvent(@NotNull final DragEvent dragEvent) { UIUtils.acceptIfHasFile(dragEvent, AUDIO_EXTENSIONS); } @Override + @FXThread protected void createComponents(@NotNull final HBox container) { super.createComponents(container); @@ -108,10 +116,17 @@ protected void createComponents(@NotNull final HBox container) { /** * Show dialog for choosing another audio key. */ + @FXThread protected void processChange() { UIUtils.openFileAssetDialog(this::addAudioData, AUDIO_EXTENSIONS, DEFAULT_ACTION_TESTER); } + /** + * Add the new audio data. + * + * @param file the audio file. + */ + @FXThread private void addAudioData(@NotNull final Path file) { final Path assetFile = notNull(getAssetFile(file)); @@ -129,6 +144,7 @@ private void addAudioData(@NotNull final Path file) { /** * Open this audio data in the audio viewer. */ + @FXThread protected void processOpen() { final AudioKey element = getPropertyValue(); @@ -152,12 +168,13 @@ protected void processOpen() { * * @return the label with name of the audio key. */ - @NotNull - private Label getAudioKeyLabel() { + @FXThread + private @NotNull Label getAudioKeyLabel() { return notNull(audioKeyLabel); } @Override + @FXThread protected void reload() { final AudioKey element = getPropertyValue(); final Label audioKeyLabel = getAudioKeyLabel(); diff --git a/src/main/java/com/ss/editor/ui/control/property/impl/BooleanPropertyControl.java b/src/main/java/com/ss/editor/ui/control/property/impl/BooleanPropertyControl.java index 6def4493..4c16e5ce 100644 --- a/src/main/java/com/ss/editor/ui/control/property/impl/BooleanPropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/property/impl/BooleanPropertyControl.java @@ -1,6 +1,8 @@ package com.ss.editor.ui.control.property.impl; import static com.ss.rlib.util.ObjectUtils.notNull; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.model.undo.editor.ChangeConsumer; import com.ss.editor.ui.control.property.PropertyControl; import com.ss.editor.ui.css.CSSClasses; @@ -40,6 +42,7 @@ public BooleanPropertyControl(@Nullable final Boolean propertyValue, @NotNull fi } @Override + @FXThread protected void createComponents(@NotNull final HBox container) { super.createComponents(container); @@ -52,6 +55,7 @@ protected void createComponents(@NotNull final HBox container) { } @Override + @FromAnyThread protected boolean isSingleRow() { return true; } @@ -59,11 +63,13 @@ protected boolean isSingleRow() { /** * @return the {@link CheckBox} with current value. */ + @FXThread private @NotNull CheckBox getCheckBox() { return notNull(checkBox); } @Override + @FXThread protected void reload() { final Boolean value = getPropertyValue(); final CheckBox checkBox = getCheckBox(); @@ -73,6 +79,7 @@ protected void reload() { /** * Update the value. */ + @FXThread private void updateValue() { if (isIgnoreListener()) return; final CheckBox checkBox = getCheckBox(); diff --git a/src/main/java/com/ss/editor/ui/control/property/impl/ColorPropertyControl.java b/src/main/java/com/ss/editor/ui/control/property/impl/ColorPropertyControl.java index 6fde6be4..e3b2a4dc 100644 --- a/src/main/java/com/ss/editor/ui/control/property/impl/ColorPropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/property/impl/ColorPropertyControl.java @@ -2,6 +2,8 @@ import static com.ss.rlib.util.ObjectUtils.notNull; import com.jme3.math.ColorRGBA; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.model.undo.editor.ChangeConsumer; import com.ss.editor.ui.control.property.PropertyControl; import com.ss.editor.ui.css.CSSClasses; @@ -33,6 +35,7 @@ public ColorPropertyControl(@Nullable final ColorRGBA propertyValue, @NotNull fi } @Override + @FXThread protected void createComponents(@NotNull final HBox container) { super.createComponents(container); @@ -45,11 +48,13 @@ protected void createComponents(@NotNull final HBox container) { } @Override + @FXThread protected void setPropertyValue(@Nullable final ColorRGBA color) { super.setPropertyValue(color == null ? null : color.clone()); } @Override + @FromAnyThread protected boolean isSingleRow() { return true; } @@ -57,11 +62,13 @@ protected boolean isSingleRow() { /** * @return the color picker. */ + @FXThread private @NotNull ColorPicker getColorPicker() { return notNull(colorPicker); } @Override + @FXThread protected void reload() { final ColorPicker colorPicker = getColorPicker(); colorPicker.setValue(UIUtils.from(getPropertyValue())); @@ -70,6 +77,7 @@ protected void reload() { /** * Updating value. */ + @FXThread private void updateValue() { if (isIgnoreListener()) return; diff --git a/src/main/java/com/ss/editor/ui/control/property/impl/DefaultPropertyControl.java b/src/main/java/com/ss/editor/ui/control/property/impl/DefaultPropertyControl.java index ce60f1cc..83c39fad 100644 --- a/src/main/java/com/ss/editor/ui/control/property/impl/DefaultPropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/property/impl/DefaultPropertyControl.java @@ -1,6 +1,8 @@ package com.ss.editor.ui.control.property.impl; import static com.ss.rlib.util.ObjectUtils.notNull; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.model.undo.editor.ChangeConsumer; import com.ss.editor.ui.control.property.PropertyControl; import com.ss.editor.ui.css.CSSClasses; @@ -44,6 +46,7 @@ public DefaultPropertyControl(@Nullable final T propertyValue, @NotNull final St * * @param toStringFunction the string function. */ + @FromAnyThread public void setToStringFunction(@Nullable final Function toStringFunction) { this.toStringFunction = toStringFunction; } @@ -51,6 +54,7 @@ public void setToStringFunction(@Nullable final Function toStringFunc /** * @return the string function. */ + @FromAnyThread private @Nullable Function getToStringFunction() { return toStringFunction; } @@ -63,6 +67,7 @@ public void setToStringFunction(@Nullable final Function toStringFunc } @Override + @FXThread protected void createComponents(@NotNull final HBox container) { super.createComponents(container); @@ -75,6 +80,7 @@ protected void createComponents(@NotNull final HBox container) { } @Override + @FXThread public void reload() { super.reload(); diff --git a/src/main/java/com/ss/editor/ui/control/property/impl/DefaultSinglePropertyControl.java b/src/main/java/com/ss/editor/ui/control/property/impl/DefaultSinglePropertyControl.java index b61c0bb7..8faeff99 100644 --- a/src/main/java/com/ss/editor/ui/control/property/impl/DefaultSinglePropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/property/impl/DefaultSinglePropertyControl.java @@ -1,5 +1,7 @@ package com.ss.editor.ui.control.property.impl; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.model.undo.editor.ChangeConsumer; import javafx.scene.layout.HBox; import org.jetbrains.annotations.NotNull; @@ -21,6 +23,7 @@ public DefaultSinglePropertyControl(@Nullable final T propertyValue, @NotNull fi } @Override + @FXThread protected void createComponents(@NotNull final HBox container) { super.createComponents(container); getPropertyValueLabel().prefWidthProperty() @@ -28,6 +31,7 @@ protected void createComponents(@NotNull final HBox container) { } @Override + @FromAnyThread protected boolean isSingleRow() { return true; } diff --git a/src/main/java/com/ss/editor/ui/control/property/impl/ElementPropertyControl.java b/src/main/java/com/ss/editor/ui/control/property/impl/ElementPropertyControl.java index 2311ab4d..80262086 100644 --- a/src/main/java/com/ss/editor/ui/control/property/impl/ElementPropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/property/impl/ElementPropertyControl.java @@ -2,6 +2,7 @@ import static com.ss.rlib.util.ObjectUtils.notNull; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; import com.ss.editor.model.undo.editor.ChangeConsumer; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.property.PropertyControl; @@ -50,6 +51,7 @@ public ElementPropertyControl(@NotNull final Class type, @Nullable final T pr } @Override + @FXThread protected void createComponents(@NotNull final HBox container) { super.createComponents(container); @@ -81,12 +83,14 @@ protected void createComponents(@NotNull final HBox container) { /** * Show dialog to choose an element. */ + @FXThread protected void processAdd() { } /** * Open this material in the material editor. */ + @FXThread protected void processRemove() { changed(null, getPropertyValue()); } @@ -96,6 +100,7 @@ protected void processRemove() { * * @return the label with name of the material. */ + @FXThread protected @NotNull Label getElementLabel() { return notNull(elementLabel); } diff --git a/src/main/java/com/ss/editor/ui/control/property/impl/EnumPropertyControl.java b/src/main/java/com/ss/editor/ui/control/property/impl/EnumPropertyControl.java index 98263106..2c2fda6a 100644 --- a/src/main/java/com/ss/editor/ui/control/property/impl/EnumPropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/property/impl/EnumPropertyControl.java @@ -2,6 +2,8 @@ import static com.ss.editor.util.EditorUtil.getAvailableValues; import static com.ss.rlib.util.ObjectUtils.notNull; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.model.undo.editor.ChangeConsumer; import com.ss.editor.ui.control.property.PropertyControl; import com.ss.editor.ui.css.CSSClasses; @@ -53,13 +55,14 @@ public EnumPropertyControl(@Nullable final E propertyValue, @NotNull final Strin } @Override + @FXThread protected void createComponents(@NotNull final HBox container) { super.createComponents(container); enumComboBox = new ComboBox<>(); enumComboBox.getSelectionModel() .selectedItemProperty() - .addListener((observable, oldValue, newValue) -> updateCullHint()); + .addListener((observable, oldValue, newValue) -> change()); enumComboBox.prefWidthProperty() .bind(widthProperty().multiply(CONTROL_WIDTH_PERCENT)); @@ -70,6 +73,7 @@ protected void createComponents(@NotNull final HBox container) { /** * @return the list of available options of the {@link Enum} value. */ + @FXThread private @NotNull ComboBox getEnumComboBox() { return notNull(enumComboBox); } @@ -77,7 +81,8 @@ protected void createComponents(@NotNull final HBox container) { /** * Update selected {@link Enum} value. */ - private void updateCullHint() { + @FXThread + private void change() { if (isIgnoreListener()) return; final ComboBox enumComboBox = getEnumComboBox(); @@ -88,6 +93,7 @@ private void updateCullHint() { } @Override + @FXThread protected void reload() { final E element = getPropertyValue(); @@ -98,6 +104,7 @@ protected void reload() { } @Override + @FromAnyThread protected boolean isSingleRow() { return true; } diff --git a/src/main/java/com/ss/editor/ui/control/property/impl/FloatPropertyControl.java b/src/main/java/com/ss/editor/ui/control/property/impl/FloatPropertyControl.java index 0fe39950..b06f0aba 100644 --- a/src/main/java/com/ss/editor/ui/control/property/impl/FloatPropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/property/impl/FloatPropertyControl.java @@ -2,6 +2,7 @@ import static com.ss.rlib.util.ObjectUtils.notNull; import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.model.undo.editor.ChangeConsumer; import com.ss.editor.ui.control.property.PropertyControl; import com.ss.editor.ui.css.CSSClasses; @@ -41,6 +42,7 @@ public FloatPropertyControl(@Nullable final Float propertyValue, @NotNull final } @Override + @FXThread protected void createComponents(@NotNull final HBox container) { super.createComponents(container); @@ -85,6 +87,7 @@ public void setMinMax(final float min, final float max) { } @Override + @FromAnyThread protected boolean isSingleRow() { return true; } @@ -92,11 +95,13 @@ protected boolean isSingleRow() { /** * @return the filed with current value. */ + @FXThread private @NotNull FloatTextField getValueField() { return notNull(valueField); } @Override + @FXThread protected void reload() { final Float value = getPropertyValue(); final FloatTextField valueField = getValueField(); @@ -108,6 +113,7 @@ protected void reload() { /** * Update the value. */ + @FXThread private void updateValue() { if (isIgnoreListener()) return; diff --git a/src/main/java/com/ss/editor/ui/control/property/impl/IntArrayPropertyControl.java b/src/main/java/com/ss/editor/ui/control/property/impl/IntArrayPropertyControl.java index fed0f959..155ea12b 100644 --- a/src/main/java/com/ss/editor/ui/control/property/impl/IntArrayPropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/property/impl/IntArrayPropertyControl.java @@ -1,6 +1,8 @@ package com.ss.editor.ui.control.property.impl; import static com.ss.rlib.util.ObjectUtils.notNull; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.model.undo.editor.ChangeConsumer; import com.ss.editor.ui.control.property.PropertyControl; import com.ss.editor.ui.css.CSSClasses; @@ -44,6 +46,7 @@ public IntArrayPropertyControl(@Nullable final int[] propertyValue, @NotNull fin } @Override + @FXThread protected void createComponents(@NotNull final HBox container) { super.createComponents(container); @@ -57,6 +60,7 @@ protected void createComponents(@NotNull final HBox container) { } @Override + @FromAnyThread protected boolean isSingleRow() { return true; } @@ -64,11 +68,13 @@ protected boolean isSingleRow() { /** * @return the filed with current value. */ + @FXThread private @NotNull TextField getValueField() { return notNull(valueField); } @Override + @FXThread protected void reload() { final int[] element = getPropertyValue(); @@ -88,6 +94,7 @@ protected void reload() { /** * Update the value. */ + @FXThread private void updateValue(@Nullable final KeyEvent event) { if (isIgnoreListener() || (event != null && event.getCode() != KeyCode.ENTER)) return; diff --git a/src/main/java/com/ss/editor/ui/control/property/impl/IntegerPropertyControl.java b/src/main/java/com/ss/editor/ui/control/property/impl/IntegerPropertyControl.java index caae67f5..0cffc73c 100644 --- a/src/main/java/com/ss/editor/ui/control/property/impl/IntegerPropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/property/impl/IntegerPropertyControl.java @@ -2,6 +2,7 @@ import static com.ss.rlib.util.ObjectUtils.notNull; import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.model.undo.editor.ChangeConsumer; import com.ss.editor.ui.control.property.PropertyControl; import com.ss.editor.ui.css.CSSClasses; @@ -32,6 +33,7 @@ public IntegerPropertyControl(@Nullable final Integer propertyValue, @NotNull fi } @Override + @FXThread protected void createComponents(@NotNull final HBox container) { super.createComponents(container); @@ -44,6 +46,7 @@ protected void createComponents(@NotNull final HBox container) { } @Override + @FromAnyThread protected boolean isSingleRow() { return true; } @@ -51,6 +54,7 @@ protected boolean isSingleRow() { /** * @return the filed with current value. */ + @FXThread private @NotNull IntegerTextField getValueField() { return notNull(valueField); } @@ -87,6 +91,7 @@ public int getScrollPower() { } @Override + @FXThread protected void reload() { final Integer element = getPropertyValue(); final IntegerTextField valueField = getValueField(); @@ -98,6 +103,7 @@ protected void reload() { /** * Update the value. */ + @FXThread private void updateValue() { if (isIgnoreListener()) return; diff --git a/src/main/java/com/ss/editor/ui/control/property/impl/LodLevelPropertyControl.java b/src/main/java/com/ss/editor/ui/control/property/impl/LodLevelPropertyControl.java index 394c2af9..19a185be 100644 --- a/src/main/java/com/ss/editor/ui/control/property/impl/LodLevelPropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/property/impl/LodLevelPropertyControl.java @@ -4,6 +4,8 @@ import com.jme3.scene.Mesh; import com.jme3.scene.VertexBuffer; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.model.undo.editor.ChangeConsumer; import com.ss.editor.ui.control.property.PropertyControl; import com.ss.editor.ui.css.CSSClasses; @@ -63,11 +65,13 @@ public LodLevelPropertyControl(@Nullable final Integer element, @NotNull final S } @Override + @FromAnyThread protected boolean isSingleRow() { return true; } @Override + @FXThread protected void createComponents(@NotNull final HBox container) { super.createComponents(container); @@ -83,14 +87,20 @@ protected void createComponents(@NotNull final HBox container) { FXUtils.addClassTo(levelComboBox, CSSClasses.ABSTRACT_PARAM_CONTROL_COMBO_BOX); } + /** + * Update lod level. + * + * @param newValue the new level. + */ + @FXThread private void updateLevel(@Nullable final Integer newValue) { if (isIgnoreListener()) return; changed(newValue == null ? 0 : newValue, getPropertyValue()); } - @NotNull @Override - public Integer getPropertyValue() { + @FXThread + public @NotNull Integer getPropertyValue() { final Integer value = super.getPropertyValue(); return value == null ? 0 : value; } @@ -100,12 +110,13 @@ public Integer getPropertyValue() { * * @return The lod level combobox. */ - @NotNull - protected ComboBox getLevelComboBox() { + @FXThread + protected @NotNull ComboBox getLevelComboBox() { return ObjectUtils.notNull(levelComboBox); } @Override + @FXThread protected void reload() { if (!hasEditObject()) return; diff --git a/src/main/java/com/ss/editor/ui/control/property/impl/MaterialKeyPropertyControl.java b/src/main/java/com/ss/editor/ui/control/property/impl/MaterialKeyPropertyControl.java index fe3273f2..3551ce59 100644 --- a/src/main/java/com/ss/editor/ui/control/property/impl/MaterialKeyPropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/property/impl/MaterialKeyPropertyControl.java @@ -3,6 +3,7 @@ import static com.ss.editor.util.EditorUtil.*; import static com.ss.rlib.util.ObjectUtils.notNull; import com.jme3.asset.MaterialKey; +import com.ss.editor.annotation.FXThread; import com.ss.editor.model.undo.editor.ChangeConsumer; import com.ss.editor.ui.event.impl.RequestedOpenFileEvent; import com.ss.editor.ui.util.UIUtils; @@ -28,11 +29,13 @@ public MaterialKeyPropertyControl(@Nullable final MaterialKey element, @NotNull super(element, paramName, changeConsumer); } + @FXThread @Override protected void processChange() { UIUtils.openFileAssetDialog(this::addMaterial, MATERIAL_EXTENSIONS, DEFAULT_ACTION_TESTER); } + @FXThread @Override protected void addMaterial(@NotNull final Path file) { @@ -48,6 +51,7 @@ protected void addMaterial(@NotNull final Path file) { } } + @FXThread @Override protected void processEdit() { @@ -67,6 +71,7 @@ protected void processEdit() { FX_EVENT_MANAGER.notify(event); } + @FXThread @Override protected void reload() { final MaterialKey element = getPropertyValue(); diff --git a/src/main/java/com/ss/editor/ui/control/property/impl/MaterialPropertyControl.java b/src/main/java/com/ss/editor/ui/control/property/impl/MaterialPropertyControl.java index 9193fdf5..e1b0a349 100644 --- a/src/main/java/com/ss/editor/ui/control/property/impl/MaterialPropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/property/impl/MaterialPropertyControl.java @@ -6,6 +6,7 @@ import com.jme3.scene.Spatial; import com.ss.editor.FileExtensions; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; import com.ss.editor.model.undo.editor.ChangeConsumer; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.property.PropertyControl; @@ -60,22 +61,28 @@ public class MaterialPropertyControl extends Pro public MaterialPropertyControl(@Nullable final V element, @NotNull final String paramName, @NotNull final C changeConsumer) { super(element, paramName, changeConsumer); - setOnDragOver(this::dragOver); - setOnDragDropped(this::dragDropped); - setOnDragExited(this::dragExited); + setOnDragOver(this::handleDragOverEvent); + setOnDragDropped(this::handleDragDroppedEvent); + setOnDragExited(this::handleDragExitedEvent); } /** - * Handle grad exiting. + * Handle grad exited events. + * + * @param dragEvent the drag exited event. */ - private void dragExited(@NotNull final DragEvent dragEvent) { + @FXThread + private void handleDragExitedEvent(@NotNull final DragEvent dragEvent) { } /** - * Handle dropped files to editor. + * Handle dropped events. + * + * @param dragEvent the dropped event. */ - private void dragDropped(@NotNull final DragEvent dragEvent) { + @FXThread + private void handleDragDroppedEvent(@NotNull final DragEvent dragEvent) { final Dragboard dragboard = dragEvent.getDragboard(); final List files = unsafeCast(dragboard.getContent(DataFormat.FILES)); @@ -94,9 +101,12 @@ private void dragDropped(@NotNull final DragEvent dragEvent) { } /** - * Handle drag over. + * Handle drag over events. + * + * @param dragEvent the drag over event. */ - private void dragOver(@NotNull final DragEvent dragEvent) { + @FXThread + private void handleDragOverEvent(@NotNull final DragEvent dragEvent) { final Dragboard dragboard = dragEvent.getDragboard(); final List files = unsafeCast(dragboard.getContent(DataFormat.FILES)); @@ -123,10 +133,12 @@ private void dragOver(@NotNull final DragEvent dragEvent) { * * @param file the file */ + @FXThread protected void addMaterial(@NotNull final Path file) { } @Override + @FXThread protected void createComponents(@NotNull final HBox container) { super.createComponents(container); @@ -161,12 +173,14 @@ protected void createComponents(@NotNull final HBox container) { /** * Show dialog for choosing another material. */ + @FXThread protected void processChange() { } /** * Open this material in the material editor. */ + @FXThread protected void processEdit() { } @@ -175,8 +189,8 @@ protected void processEdit() { * * @return the label with name of the material. */ - @NotNull - protected Label getMaterialLabel() { + @FXThread + protected @NotNull Label getMaterialLabel() { return notNull(materialLabel); } } diff --git a/src/main/java/com/ss/editor/ui/control/property/impl/MinMaxPropertyControl.java b/src/main/java/com/ss/editor/ui/control/property/impl/MinMaxPropertyControl.java index e934058d..193beacb 100644 --- a/src/main/java/com/ss/editor/ui/control/property/impl/MinMaxPropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/property/impl/MinMaxPropertyControl.java @@ -4,6 +4,7 @@ import static java.lang.Math.min; import com.jme3.math.Vector2f; import com.jme3.scene.Spatial; +import com.ss.editor.annotation.FXThread; import com.ss.editor.model.undo.editor.ChangeConsumer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -25,11 +26,13 @@ public MinMaxPropertyControl(@Nullable final Vector2f propertyValue, @NotNull fi } @Override + @FXThread protected float checkResultXValue(final float x, final float y) { return min(x, y); } @Override + @FXThread protected float checkResultYValue(final float x, final float y) { return max(x, y); } diff --git a/src/main/java/com/ss/editor/ui/control/property/impl/QuaternionPropertyControl.java b/src/main/java/com/ss/editor/ui/control/property/impl/QuaternionPropertyControl.java index 920b3590..96a19f72 100644 --- a/src/main/java/com/ss/editor/ui/control/property/impl/QuaternionPropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/property/impl/QuaternionPropertyControl.java @@ -4,6 +4,7 @@ import static com.ss.rlib.geom.util.AngleUtils.radiansToDegree; import static com.ss.rlib.util.ObjectUtils.notNull; import com.jme3.math.Quaternion; +import com.ss.editor.annotation.FXThread; import com.ss.editor.model.undo.editor.ChangeConsumer; import com.ss.editor.ui.control.property.PropertyControl; import com.ss.editor.ui.css.CSSClasses; @@ -19,7 +20,7 @@ import org.jetbrains.annotations.Nullable; /** - * The implementation of the {@link ModelPropertyControl} to edit {@link Quaternion} values. + * The implementation of the {@link PropertyControl} to edit {@link Quaternion} values. * * @param the type parameter * @param the type parameter @@ -51,6 +52,7 @@ public QuaternionPropertyControl(@Nullable final Quaternion propertyValue, @NotN } @Override + @FXThread protected void createComponents(@NotNull final HBox container) { super.createComponents(container); @@ -92,6 +94,7 @@ protected void createComponents(@NotNull final HBox container) { } @Override + @FXThread protected void setPropertyValue(@Nullable final Quaternion quaternion) { super.setPropertyValue(quaternion == null ? null : quaternion.clone()); } @@ -99,6 +102,7 @@ protected void setPropertyValue(@Nullable final Quaternion quaternion) { /** * @return the field X. */ + @FXThread private @NotNull FloatTextField getXField() { return notNull(xField); } @@ -106,6 +110,7 @@ protected void setPropertyValue(@Nullable final Quaternion quaternion) { /** * @return the field Y. */ + @FXThread private @NotNull FloatTextField getYFiled() { return notNull(yField); } @@ -113,11 +118,13 @@ protected void setPropertyValue(@Nullable final Quaternion quaternion) { /** * @return the field Z. */ + @FXThread private @NotNull FloatTextField getZField() { return notNull(zField); } @Override + @FXThread protected void reload() { final float[] angles = new float[3]; @@ -143,6 +150,7 @@ protected void reload() { /** * Updating rotation. */ + @FXThread private void updateRotation(@Nullable final KeyEvent event) { if (isIgnoreListener() || (event != null && event.getCode() != KeyCode.ENTER)) return; diff --git a/src/main/java/com/ss/editor/ui/control/property/impl/StringPropertyControl.java b/src/main/java/com/ss/editor/ui/control/property/impl/StringPropertyControl.java index 0dd8cc1b..93ab16de 100644 --- a/src/main/java/com/ss/editor/ui/control/property/impl/StringPropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/property/impl/StringPropertyControl.java @@ -1,6 +1,8 @@ package com.ss.editor.ui.control.property.impl; import static com.ss.rlib.util.ObjectUtils.notNull; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.model.undo.editor.ChangeConsumer; import com.ss.editor.ui.control.property.PropertyControl; import com.ss.editor.ui.css.CSSClasses; @@ -33,6 +35,7 @@ public StringPropertyControl(@Nullable final String propertyValue, @NotNull fina } @Override + @FXThread protected void createComponents(@NotNull final HBox container) { super.createComponents(container); @@ -45,6 +48,7 @@ protected void createComponents(@NotNull final HBox container) { } @Override + @FromAnyThread protected boolean isSingleRow() { return true; } @@ -52,11 +56,13 @@ protected boolean isSingleRow() { /** * @return the filed with current value. */ + @FXThread private @NotNull TextField getValueField() { return notNull(valueField); } @Override + @FXThread protected void reload() { final String value = getPropertyValue(); final TextField valueField = getValueField(); @@ -68,6 +74,7 @@ protected void reload() { /** * Update the value. */ + @FXThread private void updateValue(@NotNull final KeyEvent event) { if (isIgnoreListener() || event.getCode() != KeyCode.ENTER) return; diff --git a/src/main/java/com/ss/editor/ui/control/property/impl/Texture2DPropertyControl.java b/src/main/java/com/ss/editor/ui/control/property/impl/Texture2DPropertyControl.java index 02b07606..84ebfe95 100644 --- a/src/main/java/com/ss/editor/ui/control/property/impl/Texture2DPropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/property/impl/Texture2DPropertyControl.java @@ -9,6 +9,7 @@ import com.jme3.texture.Texture; import com.jme3.texture.Texture2D; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; import com.ss.editor.config.EditorConfig; import com.ss.editor.extension.property.EditablePropertyType; import com.ss.editor.model.undo.editor.ChangeConsumer; @@ -92,25 +93,32 @@ public class Texture2DPropertyControl extends Prope public Texture2DPropertyControl(@Nullable final Texture2D propertyValue, @NotNull final String propertyName, @NotNull final C changeConsumer) { super(propertyValue, propertyName, changeConsumer); - setOnDragOver(this::dragOver); - setOnDragDropped(this::dragDropped); + setOnDragOver(this::handleDragOverEvent); + setOnDragDropped(this::handleDragDroppedEvent); } /** - * Handle dropped files to editor. + * Handle drag dropped events. + * + * @param dragEvent the drag dropped event. */ - protected void dragDropped(@NotNull final DragEvent dragEvent) { + @FXThread + protected void handleDragDroppedEvent(@NotNull final DragEvent dragEvent) { UIUtils.handleDroppedFile(dragEvent, TEXTURE_EXTENSIONS, this, Texture2DPropertyControl::setTexture); } /** - * Handle drag over. + * Handle drag over events. + * + * @param dragEvent the drag over event. */ - protected void dragOver(@NotNull final DragEvent dragEvent) { + @FXThread + protected void handleDragOverEvent(@NotNull final DragEvent dragEvent) { UIUtils.acceptIfHasFile(dragEvent, TEXTURE_EXTENSIONS); } @Override + @FXThread protected void createComponents(@NotNull final HBox container) { super.createComponents(container); @@ -160,18 +168,34 @@ protected void createComponents(@NotNull final HBox container) { CSSClasses.INPUT_CONTROL_TOOLBAR_BUTTON); } + /** + * @return the disable|remove condition. + */ + @FXThread protected @NotNull BooleanBinding buildDisableRemoveCondition() { return getTexturePreview().imageProperty().isNull(); } + /** + * @return the texture label. + */ + @FXThread private @NotNull Label getTextureLabel() { return notNull(textureLabel); } + /** + * @return the texture preview. + */ + @FXThread private @NotNull ImageView getTexturePreview() { return notNull(texturePreview); } + /** + * @return the image channels preview. + */ + @FXThread private @NotNull ImageChannelPreview getTextureTooltip() { return notNull(textureTooltip); } @@ -179,6 +203,7 @@ protected void createComponents(@NotNull final HBox container) { /** * Process to remove the current texture. */ + @FXThread protected void processRemove() { setTexture(null); } @@ -186,6 +211,7 @@ protected void processRemove() { /** * Process to add a new texture. */ + @FXThread protected void processAdd() { UIUtils.openFileAssetDialog(this::setTexture, TEXTURE_EXTENSIONS, DEFAULT_ACTION_TESTER); } @@ -193,6 +219,7 @@ protected void processAdd() { /** * Process to open texture's settings. */ + @FXThread protected void openSettings() { final Texture2D texture = notNull(getPropertyValue()); @@ -223,6 +250,7 @@ protected void openSettings() { * * @param varTable the var table. */ + @FXThread private void applyChanges(@NotNull final VarTable varTable) { final Texture2D texture = notNull(getPropertyValue()); @@ -265,6 +293,7 @@ private void applyChanges(@NotNull final VarTable varTable) { * * @param file the file to new texture. */ + @FXThread protected void setTexture(@Nullable final Path file) { if (file == null) { @@ -285,6 +314,7 @@ protected void setTexture(@Nullable final Path file) { } @Override + @FXThread protected void reload() { final Texture2D texture2D = getPropertyValue(); diff --git a/src/main/java/com/ss/editor/ui/control/property/impl/Vector2FPropertyControl.java b/src/main/java/com/ss/editor/ui/control/property/impl/Vector2FPropertyControl.java index 369f0511..9e6e4332 100644 --- a/src/main/java/com/ss/editor/ui/control/property/impl/Vector2FPropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/property/impl/Vector2FPropertyControl.java @@ -2,6 +2,8 @@ import static com.ss.rlib.util.ObjectUtils.notNull; import com.jme3.math.Vector2f; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.model.undo.editor.ChangeConsumer; import com.ss.editor.ui.control.property.PropertyControl; import com.ss.editor.ui.css.CSSClasses; @@ -41,6 +43,7 @@ public Vector2FPropertyControl(@Nullable final Vector2f propertyValue, @NotNull } @Override + @FXThread protected void createComponents(@NotNull final HBox container) { super.createComponents(container); @@ -73,11 +76,13 @@ protected void createComponents(@NotNull final HBox container) { } @Override + @FXThread protected void setPropertyValue(@Nullable final Vector2f vector) { super.setPropertyValue(vector == null ? null : vector.clone()); } @Override + @FromAnyThread protected boolean isSingleRow() { return true; } @@ -89,6 +94,7 @@ protected boolean isSingleRow() { * @param y the y * @return the float */ + @FXThread protected float checkResultXValue(final float x, final float y) { return x; } @@ -100,6 +106,7 @@ protected float checkResultXValue(final float x, final float y) { * @param y the y * @return the float */ + @FXThread protected float checkResultYValue(final float x, final float y) { return y; } @@ -109,6 +116,7 @@ protected float checkResultYValue(final float x, final float y) { * * @return the field X. */ + @FXThread protected @NotNull FloatTextField getXField() { return notNull(xField); } @@ -118,11 +126,13 @@ protected float checkResultYValue(final float x, final float y) { * * @return the field Y. */ + @FXThread protected @NotNull FloatTextField getYField() { return notNull(yField); } @Override + @FXThread protected void reload() { final Vector2f vector = getPropertyValue() == null ? Vector2f.ZERO : getPropertyValue(); @@ -141,6 +151,7 @@ protected void reload() { * * @param event the event */ + @FXThread private void updateVector(@Nullable final KeyEvent event) { if (isIgnoreListener() || (event != null && event.getCode() != KeyCode.ENTER)) return; diff --git a/src/main/java/com/ss/editor/ui/control/property/impl/Vector3FPropertyControl.java b/src/main/java/com/ss/editor/ui/control/property/impl/Vector3FPropertyControl.java index f42d1042..2ca9fffe 100644 --- a/src/main/java/com/ss/editor/ui/control/property/impl/Vector3FPropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/property/impl/Vector3FPropertyControl.java @@ -2,6 +2,7 @@ import static com.ss.rlib.util.ObjectUtils.notNull; import com.jme3.math.Vector3f; +import com.ss.editor.annotation.FXThread; import com.ss.editor.model.undo.editor.ChangeConsumer; import com.ss.editor.ui.control.property.PropertyControl; import com.ss.editor.ui.css.CSSClasses; @@ -57,6 +58,7 @@ public Vector3FPropertyControl(@Nullable final Vector3f propertyValue, @NotNull } @Override + @FXThread protected void createComponents(@NotNull final HBox container) { super.createComponents(container); @@ -101,6 +103,7 @@ protected void createComponents(@NotNull final HBox container) { } @Override + @FXThread protected void setPropertyValue(@Nullable final Vector3f vector) { super.setPropertyValue(vector == null ? null : vector.clone()); } @@ -110,6 +113,7 @@ protected void setPropertyValue(@Nullable final Vector3f vector) { * * @return the scroll power. */ + @FXThread protected float getScrollPower() { return 10F; } @@ -119,6 +123,7 @@ protected float getScrollPower() { * * @return the field X. */ + @FXThread protected @NotNull FloatTextField getXField() { return notNull(xField); } @@ -128,6 +133,7 @@ protected float getScrollPower() { * * @return the field Y. */ + @FXThread protected @NotNull FloatTextField getYFiled() { return notNull(yField); } @@ -137,11 +143,13 @@ protected float getScrollPower() { * * @return the field Z. */ + @FXThread protected @NotNull FloatTextField getZField() { return notNull(zField); } @Override + @FXThread protected void reload() { final Vector3f vector = getPropertyValue() == null ? Vector3f.ZERO : getPropertyValue(); @@ -164,6 +172,7 @@ protected void reload() { * * @param event the event */ + @FXThread protected void updateVector(@Nullable final KeyEvent event) { if (isIgnoreListener() || (event != null && event.getCode() != KeyCode.ENTER)) return; diff --git a/src/main/java/com/ss/editor/ui/util/UIUtils.java b/src/main/java/com/ss/editor/ui/util/UIUtils.java index d297788d..7318be16 100644 --- a/src/main/java/com/ss/editor/ui/util/UIUtils.java +++ b/src/main/java/com/ss/editor/ui/util/UIUtils.java @@ -505,6 +505,16 @@ public static boolean isNotHotKey(@Nullable final KeyEvent event) { */ @FXThread public static void consumeIfIsNotHotKey(@Nullable final KeyEvent event) { + + if (event == null) { + return; + } + + final KeyCode code = event.getCode(); + if (code == KeyCode.ESCAPE || code == KeyCode.ENTER) { + return; + } + if (isNotHotKey(event)) { event.consume(); } From dead5dc1cfcb67825baa297c53b1d601cce96bb1 Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Tue, 12 Sep 2017 13:53:47 +0300 Subject: [PATCH 11/50] implemented virtual asset dialog, refactoring. --- build.gradle | 2 +- .../java/com/ss/editor/FileExtensions.java | 4 + .../ss/editor/manager/FileIconManager.java | 118 +++-- .../editor/manager/JMEFilePreviewManager.java | 67 ++- .../ss/editor/manager/JavaFXImageManager.java | 70 ++- .../ss/editor/manager/ResourceManager.java | 204 +++++--- .../api/dialog/GenericFactoryDialog.java | 13 + .../AssetTreeContextMenuFillerRegistry.java | 6 + .../ui/component/asset/tree/ResourceTree.java | 70 ++- .../asset/tree/ResourceTreeCell.java | 38 +- .../menu/action/OpenFileInExplorerAction.java | 13 +- .../AssetTreeMultiContextMenuFiller.java | 2 + .../AssetTreeSingleContextMenuFiller.java | 2 + .../FileAssetTreeSingleContextMenuFiller.java | 3 + ...ourceAssetTreeSingleContextMenuFiller.java | 3 + .../tree/resource/FileResourceElement.java | 7 +- .../tree/resource/FolderResourceElement.java | 13 +- .../tree/resource/ImageResourceElement.java | 9 +- .../asset/tree/resource/ResourceElement.java | 21 +- .../tree/resource/ResourceElementFactory.java | 11 +- .../creator/impl/DefaultSceneCreator.java | 7 +- .../creator/impl/EmptyFileCreator.java | 10 +- .../creator/impl/EmptyModelCreator.java | 9 +- .../creator/impl/EmptySceneCreator.java | 14 +- .../component/creator/impl/FolderCreator.java | 16 +- .../impl/material/MaterialFileCreator.java | 10 +- .../MaterialDefinitionFileCreator.java | 20 +- .../SingleColorTextureFileCreator.java | 7 +- .../impl/material/MaterialFileEditor.java | 2 +- .../virtual/tree/VirtualResourceTree.java | 237 +++++++++ .../virtual/tree/VirtualResourceTreeCell.java | 58 +++ .../FolderVirtualResourceElement.java | 45 ++ .../ObjectVirtualResourceElement.java | 17 + .../resource/RootVirtualResourceElement.java | 16 + .../tree/resource/VirtualResourceElement.java | 116 +++++ .../VirtualResourceElementFactory.java | 86 ++++ .../dialog/CreateSceneAppStateDialog.java | 34 +- .../dialog/CreateSceneFilterDialog.java | 30 +- .../MaterialEmitterPropertyControl.java | 2 +- .../dialog/CreateCustomControlDialog.java | 31 +- .../tree/dialog/GenerateTangentsDialog.java | 39 +- .../tree/dialog/LightSelectorDialog.java | 12 +- .../model/tree/dialog/NodeSelectorDialog.java | 37 +- .../tree/dialog/SpatialSelectorDialog.java | 12 +- .../animation/ExtractSubAnimationDialog.java | 48 +- .../geometry/GeometrySelectorDialog.java | 11 +- .../geometry/lod/GenerateLodLevelsDialog.java | 19 + .../tree/dialog/sky/CreateSkyDialog.java | 102 ++-- .../dialog/terrain/CreateTerrainDialog.java | 85 ++-- .../ui/dialog/AbstractSimpleEditorDialog.java | 25 +- .../ss/editor/ui/dialog/ConfirmDialog.java | 18 +- .../com/ss/editor/ui/dialog/EditorDialog.java | 2 + .../com/ss/editor/ui/dialog/RenameDialog.java | 38 +- .../ss/editor/ui/dialog/SettingsDialog.java | 73 ++- .../editor/ui/dialog/about/AboutDialog.java | 16 +- .../dialog/asset/BaseAssetEditorDialog.java | 467 ++++++++++++++++++ .../asset/{ => file}/AssetEditorDialog.java | 60 ++- .../{ => file}/FileAssetEditorDialog.java | 5 +- .../{ => file}/FolderAssetEditorDialog.java | 5 +- .../ParticlesAssetEditorDialog.java | 25 +- .../StringVirtualAssetEditorDialog.java | 47 ++ .../virtual/VirtualAssetEditorDialog.java | 172 +++++++ .../converter/ModelConverterDialog.java | 63 +-- .../OpenExternalFolderEditorDialog.java | 10 + .../ui/dialog/plugin/PluginsDialog.java | 28 +- .../ui/dialog/save/SaveAsEditorDialog.java | 18 + .../java/com/ss/editor/ui/util/UIUtils.java | 63 ++- .../java/com/ss/editor/util/EditorUtil.java | 9 +- src/main/resources/credits/icons.txt | 3 +- .../resources/ui/icons/filetypes/vector.svg | 80 +++ 70 files changed, 2448 insertions(+), 587 deletions(-) create mode 100644 src/main/java/com/ss/editor/ui/component/virtual/tree/VirtualResourceTree.java create mode 100644 src/main/java/com/ss/editor/ui/component/virtual/tree/VirtualResourceTreeCell.java create mode 100644 src/main/java/com/ss/editor/ui/component/virtual/tree/resource/FolderVirtualResourceElement.java create mode 100644 src/main/java/com/ss/editor/ui/component/virtual/tree/resource/ObjectVirtualResourceElement.java create mode 100644 src/main/java/com/ss/editor/ui/component/virtual/tree/resource/RootVirtualResourceElement.java create mode 100644 src/main/java/com/ss/editor/ui/component/virtual/tree/resource/VirtualResourceElement.java create mode 100644 src/main/java/com/ss/editor/ui/component/virtual/tree/resource/VirtualResourceElementFactory.java create mode 100644 src/main/java/com/ss/editor/ui/dialog/asset/BaseAssetEditorDialog.java rename src/main/java/com/ss/editor/ui/dialog/asset/{ => file}/AssetEditorDialog.java (95%) rename src/main/java/com/ss/editor/ui/dialog/asset/{ => file}/FileAssetEditorDialog.java (94%) rename src/main/java/com/ss/editor/ui/dialog/asset/{ => file}/FolderAssetEditorDialog.java (93%) rename src/main/java/com/ss/editor/ui/dialog/asset/{ => file}/ParticlesAssetEditorDialog.java (94%) create mode 100644 src/main/java/com/ss/editor/ui/dialog/asset/virtual/StringVirtualAssetEditorDialog.java create mode 100644 src/main/java/com/ss/editor/ui/dialog/asset/virtual/VirtualAssetEditorDialog.java create mode 100644 src/main/resources/ui/icons/filetypes/vector.svg diff --git a/build.gradle b/build.gradle index 967067c3..c748fd0e 100644 --- a/build.gradle +++ b/build.gradle @@ -59,7 +59,7 @@ dependencies { compile 'org.controlsfx:controlsfx:8.40.13' compile 'com.github.JavaSaBr:RlibFX:4.1.3' - compile 'com.github.JavaSaBr:RLib:6.3.6' + compile 'com.github.JavaSaBr:RLib:6.4.0' compile 'com.github.JavaSaBr:JME3-JFX:1.6.1' // https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 diff --git a/src/main/java/com/ss/editor/FileExtensions.java b/src/main/java/com/ss/editor/FileExtensions.java index e7c938c1..de749db8 100644 --- a/src/main/java/com/ss/editor/FileExtensions.java +++ b/src/main/java/com/ss/editor/FileExtensions.java @@ -19,6 +19,10 @@ public interface FileExtensions { * The constant JME_MATERIAL_DEFINITION. */ String JME_MATERIAL_DEFINITION = "j3md"; + /** + * The constant JME_SHADER_NODE. + */ + String JME_SHADER_NODE = "j3sn"; /** * The constant JME_OBJECT. */ diff --git a/src/main/java/com/ss/editor/manager/FileIconManager.java b/src/main/java/com/ss/editor/manager/FileIconManager.java index 4a2bb1a1..e50a1244 100644 --- a/src/main/java/com/ss/editor/manager/FileIconManager.java +++ b/src/main/java/com/ss/editor/manager/FileIconManager.java @@ -75,6 +75,7 @@ public class FileIconManager { EXTENSION_TO_CONTENT_TYPE.put("java", "application-x-java"); EXTENSION_TO_CONTENT_TYPE.put(FileExtensions.JME_OBJECT, "jme3"); + EXTENSION_TO_CONTENT_TYPE.put(FileExtensions.JME_SHADER_NODE, "vector"); EXTENSION_TO_CONTENT_TYPE.put(FileExtensions.JME_SCENE, "sse"); EXTENSION_TO_CONTENT_TYPE.put(FileExtensions.JME_MATERIAL, "parquet"); EXTENSION_TO_CONTENT_TYPE.put(FileExtensions.JME_MATERIAL_DEFINITION, "text"); @@ -107,9 +108,6 @@ public class FileIconManager { MIME_TYPES_FOLDERS.add(Paths.get("/ui/icons/filetypes/")); } - private static boolean supportedRepaint = true; - - @Nullable private static FileIconManager instance; @@ -118,8 +116,8 @@ public class FileIconManager { * * @return the instance */ - @NotNull - public static FileIconManager getInstance() { + @FromAnyThread + public static @NotNull FileIconManager getInstance() { if (instance == null) instance = new FileIconManager(); return instance; } @@ -174,9 +172,23 @@ public void register(@NotNull final BiFunction iconFinder) * @param size the icon size. * @return the icon. */ - @NotNull @FXThread - public Image getIcon(@NotNull final Path path, int size) { + public @NotNull Image getIcon(@NotNull final Path path, int size) { + return getIcon(path, Files.isDirectory(path), true, size); + } + + /** + * Get an icon to a file. + * + * @param path the file. + * @param directory the directory. + * @param tryToGetContentType true of we can try to get content type of the file. + * @param size the icon size. + * @return the icon. + */ + @FXThread + public @NotNull Image getIcon(@NotNull final Path path, final boolean directory, final boolean tryToGetContentType, + int size) { final String extension = FileUtils.getExtension(path); final Array> iconFinders = getIconFinders(); @@ -195,18 +207,26 @@ public Image getIcon(@NotNull final Path path, int size) { } } - String contentType = EXTENSION_TO_CONTENT_TYPE.get(extension); + String contentType; - if (contentType == null) { - try { - contentType = Files.probeContentType(path); - } catch (IOException e) { - LOGGER.warning(e); + if (directory) { + contentType = "folder"; + } else { + + contentType = EXTENSION_TO_CONTENT_TYPE.get(extension); + + if (contentType == null && tryToGetContentType) { + try { + contentType = Files.probeContentType(path); + } catch (final IOException e) { + LOGGER.warning(e); + } } } - if (Files.isDirectory(path)) contentType = "folder"; - if (contentType != null) contentType = contentType.replace("/", "-"); + if (contentType != null) { + contentType = contentType.replace("/", "-"); + } if (contentType == null) { LOGGER.debug("not found content type for " + path); @@ -249,15 +269,15 @@ public Image getIcon(@NotNull final Path path, int size) { return getImage(url, size); } + /** * Get an image by an URL. * * @param url the url. * @return the image. */ - @NotNull @FXThread - public Image getImage(@NotNull final String url) { + public @NotNull Image getImage(@NotNull final String url) { return getImage(url, 16); } @@ -268,12 +288,24 @@ public Image getImage(@NotNull final String url) { * @param size the size. * @return the image. */ - @NotNull @FXThread - public Image getImage(@NotNull final String url, final int size) { + public @NotNull Image getImage(@NotNull final String url, final int size) { return getImage(url, size, true); } + /** + * Get an image by an URL. + * + * @param url the url. + * @param classLoader the class loader. + * @param size the size. + * @return the image. + */ + @FXThread + public @NotNull Image getImage(@NotNull final String url, @NotNull final ClassLoader classLoader, final int size) { + return getImage(url, classLoader, size, true); + } + /** * Get an image by an URL. * @@ -282,22 +314,39 @@ public Image getImage(@NotNull final String url, final int size) { * @param useCache true if need to use cache. * @return the image. */ - @NotNull @FXThread - public Image getImage(@NotNull final String url, final int size, final boolean useCache) { - if (!useCache) return buildImage(url, size); + public @NotNull Image getImage(@NotNull final String url, final int size, final boolean useCache) { + return getImage(url, getClass().getClassLoader(), size, useCache); + } + + /** + * Get an image by an URL. + * + * @param url the url. + * @param classLoader the class loader. + * @param size the size. + * @param useCache true if need to use cache. + * @return the image. + */ + @FXThread + public @NotNull Image getImage(@NotNull final String url, @NotNull final ClassLoader classLoader, final int size, + final boolean useCache) { + + if (!useCache) return buildImage(url, classLoader, size); + final ObjectDictionary cache = imageCache.get(size, DictionaryFactory::newObjectDictionary); - final Image image = cache.get(url, () -> buildImage(url, size)); + final Image image = cache.get(url, () -> buildImage(url, classLoader, size)); + return notNull(image); } - @NotNull - private Image buildImage(@NotNull final String url, final int size) { - return buildImage(url, null, size); + private @NotNull Image buildImage(@NotNull final String url, @NotNull final ClassLoader classLoader, + final int size) { + final InputStream in = EditorUtil.getInputStream(url, classLoader); + return buildImage(url, in, size); } - @NotNull - private Image buildImage(@NotNull final String url, @Nullable final InputStream in, final int size) { + private @NotNull Image buildImage(@NotNull final String url, @Nullable final InputStream in, final int size) { final Image image; @@ -315,7 +364,7 @@ private Image buildImage(@NotNull final String url, @Nullable final InputStream final EditorConfig config = EditorConfig.getInstance(); final CssColorTheme theme = config.getTheme(); - if (theme.needRepaintIcons() && supportedRepaint) { + if (theme.needRepaintIcons()) { try { final Color iconColor = theme.getIconColor(); @@ -338,8 +387,7 @@ private Image buildImage(@NotNull final String url, @Nullable final InputStream return coloredImage; } catch (final Throwable e) { - e.printStackTrace(); - supportedRepaint = false; + LOGGER.warning(e); } } @@ -354,16 +402,16 @@ private Image buildImage(@NotNull final String url, @Nullable final InputStream * @param image the image. * @return the original image. */ - @NotNull - public Image getOriginal(@NotNull final Image image) { + @FromAnyThread + public @NotNull Image getOriginal(@NotNull final Image image) { return notNull(originalImageCache.get(image), "not found original for " + image.impl_getUrl()); } /** * @return the list of icon finders. */ - @NotNull - private Array> getIconFinders() { + @FromAnyThread + private @NotNull Array> getIconFinders() { return iconFinders; } } diff --git a/src/main/java/com/ss/editor/manager/JMEFilePreviewManager.java b/src/main/java/com/ss/editor/manager/JMEFilePreviewManager.java index 5c8b220b..75e11298 100644 --- a/src/main/java/com/ss/editor/manager/JMEFilePreviewManager.java +++ b/src/main/java/com/ss/editor/manager/JMEFilePreviewManager.java @@ -38,6 +38,7 @@ import com.ss.rlib.logging.Logger; import com.ss.rlib.logging.LoggerManager; import com.ss.rlib.ui.util.FXUtils; +import com.ss.rlib.util.StringUtils; import com.ss.rlib.util.array.Array; import com.ss.rlib.util.array.ArrayFactory; import javafx.scene.image.ImageView; @@ -92,8 +93,8 @@ public class JMEFilePreviewManager extends AbstractControl { * * @return the instance */ - @NotNull - public static JMEFilePreviewManager getInstance() { + @FromAnyThread + public static @NotNull JMEFilePreviewManager getInstance() { if (instance == null) { synchronized (JMEFilePreviewManager.class) { if (instance == null) { @@ -105,11 +106,12 @@ public static JMEFilePreviewManager getInstance() { } /** - * Is jme file boolean. + * Check the file. * * @param file the file * @return true is the file is a JME file. */ + @FromAnyThread public static boolean isJmeFile(@Nullable final Path file) { if (file == null) return false; final String extension = getExtension(file); @@ -117,17 +119,44 @@ public static boolean isJmeFile(@Nullable final Path file) { } /** - * Is audio file boolean. + * Check the file by the asset path. * - * @param file the file + * @param assetPath the asset path. * @return true is the file is a JME file. */ + @FromAnyThread + public static boolean isJmeFile(@Nullable final String assetPath) { + if (StringUtils.isEmpty(assetPath)) return false; + final String extension = getExtension(assetPath); + return JME_FORMATS.contains(extension); + } + + /** + * Check the file. + * + * @param file the file + * @return true is the file is an audio file. + */ + @FromAnyThread public static boolean isAudioFile(@Nullable final Path file) { if (file == null) return false; final String extension = getExtension(file); return AUDIO_FORMATS.contains(extension); } + /** + * Check the asset path. + * + * @param assetPath the asset path + * @return true is the asset path is an audio file. + */ + @FromAnyThread + public static boolean isAudioFile(@Nullable final String assetPath) { + if (assetPath == null) return false; + final String extension = getExtension(assetPath); + return AUDIO_FORMATS.contains(extension); + } + @NotNull private final JobProgressAdapter probeHandler = new JobProgressAdapter() { @@ -188,6 +217,7 @@ private JMEFilePreviewManager() { } @Override + @JMEThread protected void controlUpdate(final float tpf) { if (frame == 2) { @@ -206,6 +236,7 @@ private void notifyProbeComplete() { } @Override + @JMEThread protected void controlRender(@NotNull final RenderManager renderManager, @NotNull final ViewPort viewPort) { } @@ -223,8 +254,18 @@ public void show(@NotNull final Path file, final int fitWidth, final int fitHeig final Path assetFile = notNull(getAssetFile(file), "File can't be null."); final String path = toAssetPath(assetFile); - final String extension = getExtension(assetFile); + showPreview(path, getExtension(assetFile)); + } + + /** + * Show a preview of the file by the asset path. + * + * @param path the asset path. + * @param extension the extension. + */ + @FromAnyThread + private void showPreview(@NotNull final String path, @NotNull final String extension) { if (FileExtensions.JME_MATERIAL.equals(extension)) { EDITOR_THREAD_EXECUTOR.addToExecute(() -> showMaterial(path)); } else if (FileExtensions.JME_OBJECT.equals(extension)) { @@ -234,6 +275,20 @@ public void show(@NotNull final Path file, final int fitWidth, final int fitHeig } } + /** + * Show a file. + * + * @param assetPath the asset path. + * @param fitWidth the target width of preview. + * @param fitHeight the target height of preview. + */ + @FromAnyThread + public void show(@NotNull final String assetPath, final int fitWidth, final int fitHeight) { + imageView.setFitHeight(fitHeight); + imageView.setFitWidth(fitWidth); + showPreview(assetPath, getExtension(assetPath)); + } + /** * Show a j3o object. * diff --git a/src/main/java/com/ss/editor/manager/JavaFXImageManager.java b/src/main/java/com/ss/editor/manager/JavaFXImageManager.java index fac48ffd..66250759 100644 --- a/src/main/java/com/ss/editor/manager/JavaFXImageManager.java +++ b/src/main/java/com/ss/editor/manager/JavaFXImageManager.java @@ -1,5 +1,6 @@ package com.ss.editor.manager; +import static com.ss.rlib.util.FileUtils.getExtension; import static com.ss.rlib.util.array.ArrayFactory.asArray; import static java.awt.Image.SCALE_DEFAULT; import static java.nio.file.StandardOpenOption.*; @@ -41,7 +42,6 @@ import java.io.IOException; import java.io.OutputStream; import java.net.URL; -import java.net.URLClassLoader; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.FileTime; @@ -94,30 +94,43 @@ public class JavaFXImageManager { } /** - * Is image boolean. + * Check the file. * * @param file the file * @return true if the file is image. */ + @FromAnyThread public static boolean isImage(@Nullable final Path file) { if (file == null) return false; - final String extension = FileUtils.getExtension(file); + final String extension = getExtension(file); return IMAGE_FORMATS.contains(extension); } - @Nullable - private static JavaFXImageManager instance; - /** - * Gets instance. + * Check the file by the asset path. * - * @return the instance + * @param assetPath the asset path + * @return true if the file is image. */ + @FromAnyThread + public static boolean isImage(@Nullable final String assetPath) { + if (assetPath == null) return false; + final String extension = getExtension(assetPath); + return IMAGE_FORMATS.contains(extension); + } + + @Nullable + private static JavaFXImageManager instance; + + @FromAnyThread public static @NotNull JavaFXImageManager getInstance() { if (instance == null) instance = new JavaFXImageManager(); return instance; } + /** + * The cache of small images. + */ @NotNull private IntegerDictionary>> smallImageCache; @@ -238,41 +251,18 @@ private void putImageToCache(@NotNull final String path, @NotNull final Image im if (image != null) return image; } - final Array<@NotNull ClassLoader> classLoaders = ArrayFactory.newArray(ClassLoader.class); - classLoaders.add(getClass().getClassLoader()); + final ResourceManager resourceManager = ResourceManager.getInstance(); + URL url = resourceManager.tryToFindResource(resourcePath); - final ClasspathManager classpathManager = ClasspathManager.getInstance(); - final URLClassLoader classesLoader = classpathManager.getClassesLoader(); - final URLClassLoader librariesLoader = classpathManager.getLibrariesLoader(); - - if (classesLoader != null) { - classLoaders.add(classesLoader); - } - - if (librariesLoader != null) { - classLoaders.add(librariesLoader); - } - - final PluginManager pluginManager = PluginManager.getInstance(); - pluginManager.handlePlugins(plugin -> classLoaders.add(plugin.getClassLoader())); - - final String altResourcePath = "/" + resourcePath; + if (url == null) { - URL url = null; + final Path realFile = EditorUtil.getRealFile(resourcePath); - for (final ClassLoader classLoader : classLoaders) { - url = classLoader.getResource(resourcePath); - if (url != null) break; - url = classLoader.getResource(altResourcePath); - if (url != null) break; - } - - if (url == null) { - url = getClass().getResource("/" + resourcePath); - } + if (realFile == null || !Files.exists(realFile)) { + return Icons.IMAGE_512; + } - if (url == null) { - return Icons.IMAGE_512; + url = FileUtils.toUrl(realFile); } final Image image = getImagePreview(url, null, width, height); @@ -307,7 +297,7 @@ private void putImageToCache(@NotNull final String path, @NotNull final Image im Utils.run(cacheFile, first -> Files.createDirectories(first.getParent())); - final String extension = FileUtils.getExtension(externalForm); + final String extension = getExtension(externalForm); if (FX_FORMATS.contains(extension)) { return readFXImage(width, height, externalForm, cacheFile); diff --git a/src/main/java/com/ss/editor/manager/ResourceManager.java b/src/main/java/com/ss/editor/manager/ResourceManager.java index af274078..2d306d32 100644 --- a/src/main/java/com/ss/editor/manager/ResourceManager.java +++ b/src/main/java/com/ss/editor/manager/ResourceManager.java @@ -3,7 +3,6 @@ import static com.ss.editor.FileExtensions.*; import static com.ss.editor.util.EditorUtil.*; import static com.ss.rlib.util.ArrayUtils.contains; -import static com.ss.rlib.util.ArrayUtils.move; import static com.ss.rlib.util.FileUtils.getFiles; import static com.ss.rlib.util.FileUtils.toUrl; import static com.ss.rlib.util.ObjectUtils.notNull; @@ -83,11 +82,7 @@ public class ResourceManager extends EditorThread implements AssetEventListener @Nullable private static ResourceManager instance; - /** - * Gets instance. - * - * @return the instance - */ + @FromAnyThread public static @NotNull ResourceManager getInstance() { if (instance == null) instance = new ResourceManager(); return instance; @@ -100,34 +95,34 @@ public class ResourceManager extends EditorThread implements AssetEventListener private final ObjectDictionary assetCacheTable; /** - * The list of additional ENVs. + * The table with interested resources. */ @NotNull - private final Array additionalEnvs; + private final ObjectDictionary> interestedResources; /** - * The list of an additional classpath. + * The table with interested resources in the classpath. */ @NotNull - private final Array classLoaders; + private final ObjectDictionary> interestedResourcesInClasspath; /** - * The list of resources in the classpath. + * The list of additional ENVs. */ @NotNull - private final Array resourcesInClasspath; + private final Array additionalEnvs; /** - * The list of material definitions in the classpath. + * The list of an additional classpath. */ @NotNull - private final Array materialDefinitionsInClasspath; + private final Array classLoaders; /** - * The list of available material definitions in the classpath. + * The list of resources in the classpath. */ @NotNull - private final Array materialDefinitions; + private final Array resourcesInClasspath; /** * The list of keys for watching to folders. @@ -148,13 +143,12 @@ private ResourceManager() { this.watchKeys = ArrayFactory.newArray(WatchKey.class); this.classLoaders = ArrayFactory.newArray(URLClassLoader.class); this.resourcesInClasspath = classpathManager.getAllResources(); - this.materialDefinitionsInClasspath = ArrayFactory.newArray(String.class); - this.materialDefinitions = ArrayFactory.newArray(String.class); - - prepareClasspathResources(); + this.interestedResources = DictionaryFactory.newObjectDictionary(); + this.interestedResourcesInClasspath = DictionaryFactory.newObjectDictionary(); final ExecutorManager executorManager = ExecutorManager.getInstance(); executorManager.addFXTask(() -> { + prepareClasspathResources(); final FXEventManager fxEventManager = FXEventManager.getInstance(); fxEventManager.addEventHandler(ChangedCurrentAssetFolderEvent.EVENT_TYPE, event -> processChangeAsset()); fxEventManager.addEventHandler(RequestedRefreshAssetEvent.EVENT_TYPE, event -> processRefreshAsset()); @@ -168,10 +162,93 @@ private ResourceManager() { assetManager.addAssetEventListener(this); }); + registerInterestedFileType(FileExtensions.JME_MATERIAL_DEFINITION); + registerInterestedFileType(FileExtensions.JME_SHADER_NODE); updateAdditionalEnvs(); start(); } + /** + * Try to find a resource by the resource path. + * + * @param resourcePath the resource path. + * @return the URL or null. + */ + @FromAnyThread + public @Nullable URL tryToFindResource(@NotNull final String resourcePath) { + + final Array<@NotNull ClassLoader> classLoaders = ArrayFactory.newArray(ClassLoader.class); + classLoaders.add(getClass().getClassLoader()); + + final ClasspathManager classpathManager = ClasspathManager.getInstance(); + final URLClassLoader classesLoader = classpathManager.getClassesLoader(); + final URLClassLoader librariesLoader = classpathManager.getLibrariesLoader(); + + if (classesLoader != null) { + classLoaders.add(classesLoader); + } + + if (librariesLoader != null) { + classLoaders.add(librariesLoader); + } + + final PluginManager pluginManager = PluginManager.getInstance(); + pluginManager.handlePlugins(plugin -> classLoaders.add(plugin.getClassLoader())); + + final String altResourcePath = "/" + resourcePath; + + URL url = null; + + for (final ClassLoader classLoader : classLoaders) { + url = classLoader.getResource(resourcePath); + if (url != null) break; + url = classLoader.getResource(altResourcePath); + if (url != null) break; + } + + if (url == null) { + url = getClass().getResource("/" + resourcePath); + } + + return url; + } + + /** + * @return the table with interested resources. + */ + @FromAnyThread + private @NotNull ObjectDictionary> getInterestedResources() { + return interestedResources; + } + + /** + * @return the table with interested resources in the classpath. + */ + @FromAnyThread + private @NotNull ObjectDictionary> getInterestedResourcesInClasspath() { + return interestedResourcesInClasspath; + } + + /** + * Register the file type of interested resources. + * + * @param fileExtension the file type. + */ + public synchronized void registerInterestedFileType(@NotNull final String fileExtension) { + + ObjectDictionary> resources = getInterestedResources(); + + if(!resources.containsKey(fileExtension)) { + resources.put(fileExtension, ArrayFactory.newArray(String.class)); + } + + resources = getInterestedResourcesInClasspath(); + + if(!resources.containsKey(fileExtension)) { + resources.put(fileExtension, ArrayFactory.newArray(String.class)); + } + } + @Override @FromAnyThread public synchronized void assetLoaded(@NotNull final AssetKey key) { @@ -264,13 +341,17 @@ private synchronized void processEvent(@NotNull final DeletedFileEvent event) { final Path file = event.getFile(); final String extension = FileUtils.getExtension(file); + final ObjectDictionary> interestedResources = getInterestedResources(); + final Array resources = interestedResources.get(extension); + final Path assetFile = notNull(getAssetFile(file), "Not found asset file for " + file); final String assetPath = toAssetPath(assetFile); - if (extension.endsWith(FileExtensions.JME_MATERIAL_DEFINITION)) { - final Array materialDefinitions = getMaterialDefinitions(); - materialDefinitions.fastRemove(assetPath); - } else if (extension.endsWith(FileExtensions.JAVA_LIBRARY)) { + if (resources != null) { + resources.fastRemove(assetPath); + } + + if (extension.endsWith(FileExtensions.JAVA_LIBRARY)) { final Editor editor = Editor.getInstance(); final AssetManager assetManager = editor.getAssetManager(); @@ -296,14 +377,6 @@ private synchronized void processEvent(@NotNull final CreatedFileEvent event) { handleFile(event.getFile()); } - /** - * @return the list of material definitions in the classpath. - */ - @FromAnyThread - private @NotNull Array getMaterialDefinitionsInClasspath() { - return materialDefinitionsInClasspath; - } - /** * @return the list of resources in the classpath. */ @@ -317,23 +390,18 @@ private synchronized void processEvent(@NotNull final CreatedFileEvent event) { */ @FromAnyThread private void prepareClasspathResources() { - final Array materialDefinitionsInClasspath = getMaterialDefinitionsInClasspath(); + + final ObjectDictionary> resources = getInterestedResourcesInClasspath(); final Array resourcesInClasspath = getResourcesInClasspath(); resourcesInClasspath.forEach(resource -> { - if (resource.endsWith(FileExtensions.JME_MATERIAL_DEFINITION)) { - materialDefinitionsInClasspath.add(resource); + final String extension = FileUtils.getExtension(resource); + final Array toStore = resources.get(extension); + if (toStore != null) { + toStore.add(resource); } }); } - /** - * @return the list of available material definitions in the classpath. - */ - @FromAnyThread - private @NotNull Array getMaterialDefinitions() { - return materialDefinitions; - } - /** * Gets class loaders. * @@ -345,33 +413,43 @@ private void prepareClasspathResources() { } /** - * Gets available material definitions. + * Get available resources by the file extension. * + * @param extension the interested extension. * @return the list of all available material definitions. */ @FromAnyThread - public synchronized @NotNull Array getAvailableMaterialDefinitions() { + public synchronized @NotNull Array getAvailableResources(@NotNull final String extension) { final Array result = ArrayFactory.newArray(String.class); - addAvailableMaterialDefinitionsTo(result); + addAvailableResources(result, extension); return result; } /** - * Add available material definitions to the result array. + * Add available interested resources to the result array by the file extension. * - * @param result the result + * @param result the array to store result. + * @param extension the interested extension. */ @FromAnyThread - public synchronized void addAvailableMaterialDefinitionsTo(@NotNull final Array result) { + public synchronized void addAvailableResources(@NotNull final Array result, + @NotNull final String extension) { - final Array materialDefinitions = getMaterialDefinitions(); + final ObjectDictionary> resourcesInClasspath = getInterestedResourcesInClasspath(); + final ObjectDictionary> resources = getInterestedResources(); - move(materialDefinitions, result, false); + final Array inAsset = resources.get(extension); + final Array inClassPath = resourcesInClasspath.get(extension); - final Array materialDefinitionsInClasspath = getMaterialDefinitionsInClasspath(); - materialDefinitionsInClasspath.forEach(result, (resource, container) -> { - if (!container.contains(resource)) container.add(resource); - }); + if (inAsset != null) { + result.addAll(inAsset); + } + + if (inClassPath != null) { + inClassPath.forEach(result, + (resource, toStore) -> !toStore.contains(resource), + (resource, toStore) -> toStore.add(resource)); + } result.sort(STRING_ARRAY_COMPARATOR); } @@ -398,8 +476,8 @@ public synchronized void reload() { assetManager.clearCache(); - final Array materialDefinitions = getMaterialDefinitions(); - materialDefinitions.clear(); + final ObjectDictionary> interestedResources = getInterestedResources(); + interestedResources.forEach((extension, resources) -> resources.clear()); final EditorConfig editorConfig = EditorConfig.getInstance(); final Path currentAsset = editorConfig.getCurrentAsset(); @@ -433,11 +511,15 @@ private synchronized void handleFile(@NotNull final Path file) { final String extension = FileUtils.getExtension(file); - if (extension.endsWith(FileExtensions.JME_MATERIAL_DEFINITION)) { + final ObjectDictionary> interestedResources = getInterestedResources(); + final Array toStore = interestedResources.get(extension); + + if (toStore != null) { final Path assetFile = notNull(getAssetFile(file), "Not found asset file for " + file); - final Array materialDefinitions = getMaterialDefinitions(); - materialDefinitions.add(toAssetPath(assetFile)); - } else if (extension.endsWith(FileExtensions.JAVA_LIBRARY)) { + toStore.add(toAssetPath(assetFile)); + } + + if (extension.endsWith(FileExtensions.JAVA_LIBRARY)) { final Editor editor = Editor.getInstance(); final AssetManager assetManager = editor.getAssetManager(); diff --git a/src/main/java/com/ss/editor/plugin/api/dialog/GenericFactoryDialog.java b/src/main/java/com/ss/editor/plugin/api/dialog/GenericFactoryDialog.java index 3f4f482d..1e38ee68 100644 --- a/src/main/java/com/ss/editor/plugin/api/dialog/GenericFactoryDialog.java +++ b/src/main/java/com/ss/editor/plugin/api/dialog/GenericFactoryDialog.java @@ -3,6 +3,8 @@ import static com.ss.editor.plugin.api.property.control.PropertyEditorControlFactory.build; import static com.ss.rlib.util.ObjectUtils.notNull; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.plugin.api.property.PropertyDefinition; import com.ss.editor.ui.dialog.AbstractSimpleEditorDialog; import com.ss.editor.plugin.api.property.control.PropertyEditorControl; @@ -86,6 +88,7 @@ public GenericFactoryDialog(@NotNull final Array definitions * * @param title the new title. */ + @FXThread public void setTitle(@NotNull final String title) { getDialog().setTitle(title); } @@ -95,6 +98,7 @@ public void setTitle(@NotNull final String title) { * * @param text the new text. */ + @FXThread public void setButtonOkText(@NotNull final String text) { getOkButton().setText(text); } @@ -104,16 +108,19 @@ public void setButtonOkText(@NotNull final String text) { * * @param text the new text. */ + @FXThread public void setButtonCloseText(@NotNull final String text) { getCloseButton().setText(text); } @Override + @FromAnyThread protected @NotNull String getButtonOkText() { return Messages.SIMPLE_DIALOG_BUTTON_CREATE; } @Override + @FXThread protected void createContent(@NotNull final VBox root) { super.createContent(root); this.root = root; @@ -124,6 +131,7 @@ protected void createContent(@NotNull final VBox root) { * * @return the root. */ + @FXThread private @NotNull VBox getRoot() { return notNull(root); } @@ -131,6 +139,7 @@ protected void createContent(@NotNull final VBox root) { /** * Create controls. */ + @FXThread private void createControls() { final ObservableList children = getRoot().getChildren(); @@ -144,6 +153,7 @@ private void createControls() { } @Override + @FromAnyThread protected @NotNull Point getSize() { return DIALOG_SIZE; } @@ -151,6 +161,7 @@ private void createControls() { /** * @return the list of all definitions. */ + @FXThread private @NotNull Array getDefinitions() { return definitions; } @@ -158,11 +169,13 @@ private void createControls() { /** * Validate current values. */ + @FXThread protected void validate() { getOkButton().setDisable(!validator.test(vars)); } @Override + @FXThread protected void processOk() { super.processOk(); handler.accept(vars); diff --git a/src/main/java/com/ss/editor/ui/component/asset/tree/AssetTreeContextMenuFillerRegistry.java b/src/main/java/com/ss/editor/ui/component/asset/tree/AssetTreeContextMenuFillerRegistry.java index efc93d00..c845a7d6 100644 --- a/src/main/java/com/ss/editor/ui/component/asset/tree/AssetTreeContextMenuFillerRegistry.java +++ b/src/main/java/com/ss/editor/ui/component/asset/tree/AssetTreeContextMenuFillerRegistry.java @@ -24,9 +24,15 @@ public static AssetTreeContextMenuFillerRegistry getInstance() { return INSTANCE; } + /** + * The list of single fillers. + */ @NotNull private final Array singleFillers; + /** + * The list of multi fillers. + */ @NotNull private final Array multiFillers; diff --git a/src/main/java/com/ss/editor/ui/component/asset/tree/ResourceTree.java b/src/main/java/com/ss/editor/ui/component/asset/tree/ResourceTree.java index 09411f57..5a47e596 100644 --- a/src/main/java/com/ss/editor/ui/component/asset/tree/ResourceTree.java +++ b/src/main/java/com/ss/editor/ui/component/asset/tree/ResourceTree.java @@ -6,6 +6,7 @@ import static com.ss.rlib.util.ObjectUtils.notNull; import com.ss.editor.annotation.BackgroundThread; import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.config.EditorConfig; import com.ss.editor.manager.ExecutorManager; import com.ss.editor.ui.FXConstants; @@ -44,13 +45,22 @@ * @author JavaSaBr */ public class ResourceTree extends TreeView { - + + /** + * The executor manager. + */ @NotNull private static final ExecutorManager EXECUTOR_MANAGER = ExecutorManager.getInstance(); + /** + * The resource elements comparator. + */ @NotNull private static final ArrayComparator COMPARATOR = ResourceElement::compareTo; - + + /** + * The name comparator. + */ @NotNull private static final ArrayComparator NAME_COMPARATOR = (first, second) -> { @@ -68,28 +78,31 @@ public class ResourceTree extends TreeView { return StringUtils.compareIgnoreCase(firstName, secondName); }; + /** + * The tree items comparator. + */ @NotNull private static final ArrayComparator> ITEM_COMPARATOR = (first, second) -> { - final ResourceElement firstElement = notNull(first).getValue(); final ResourceElement secondElement = notNull(second).getValue(); - - final int firstLevel = getLevel(firstElement); - final int secondLevel = getLevel(secondElement); - - if (firstLevel != secondLevel) return firstLevel - secondLevel; - return NAME_COMPARATOR.compare(firstElement, secondElement); }; + /** + * The context menu filler registry. + */ @NotNull private static final AssetTreeContextMenuFillerRegistry CONTEXT_MENU_FILLER_REGISTRY = AssetTreeContextMenuFillerRegistry.getInstance(); + @FromAnyThread private static int getLevel(@Nullable final ResourceElement element) { if (element instanceof FolderResourceElement) return 1; return 2; } + /** + * The default open function. + */ @NotNull private static final Consumer DEFAULT_OPEN_FUNCTION = element -> { final OpenFileAction action = new OpenFileAction(element); @@ -186,6 +199,7 @@ public ResourceTree(@Nullable final Consumer openFunction, fina /** * @param lazyMode true if need to use lazy mode. */ + @FromAnyThread public void setLazyMode(final boolean lazyMode) { this.lazyMode = lazyMode; } @@ -193,6 +207,7 @@ public void setLazyMode(final boolean lazyMode) { /** * @return true if need to use lazy mode. */ + @FromAnyThread private boolean isLazyMode() { return lazyMode; } @@ -200,6 +215,7 @@ private boolean isLazyMode() { /** * @param needCleanup true of need to cleanup this tree. */ + @FromAnyThread public void setNeedCleanup(final boolean needCleanup) { this.needCleanup = needCleanup; } @@ -207,6 +223,7 @@ public void setNeedCleanup(final boolean needCleanup) { /** * @return true of need to cleanup this tree. */ + @FromAnyThread private boolean isNeedCleanup() { return needCleanup; } @@ -214,6 +231,7 @@ private boolean isNeedCleanup() { /** * Handle changed count of expanded elements. */ + @FXThread private void processChangedExpands(@NotNull final Number newValue) { if (isLazyMode()) { @@ -296,6 +314,7 @@ private void lazyLoadChildren(@NotNull final TreeItem treeItem, * * @param expandHandler the handler for listening expand items. */ + @FromAnyThread public void setExpandHandler(@Nullable final IntObjectConsumer expandHandler) { this.expandHandler = expandHandler; } @@ -305,6 +324,7 @@ public void setExpandHandler(@Nullable final IntObjectConsumer exp * * @param actionTester the action tester. */ + @FromAnyThread public void setActionTester(@NotNull final Predicate> actionTester) { this.actionTester = actionTester; } @@ -312,6 +332,7 @@ public void setActionTester(@NotNull final Predicate> actionTester) { /** * @return the handler for listening expand items. */ + @FromAnyThread private @Nullable IntObjectConsumer getExpandHandler() { return expandHandler; } @@ -321,6 +342,7 @@ public void setActionTester(@NotNull final Predicate> actionTester) { * * @param extensionFilter the list of filtered extensions. */ + @FromAnyThread public void setExtensionFilter(@NotNull final Array extensionFilter) { this.extensionFilter = extensionFilter; } @@ -328,6 +350,7 @@ public void setExtensionFilter(@NotNull final Array extensionFilter) { /** * @return the list of filtered extensions. */ + @FromAnyThread private @NotNull Array getExtensionFilter() { return extensionFilter; } @@ -337,6 +360,7 @@ public void setExtensionFilter(@NotNull final Array extensionFilter) { * * @param onLoadHandler the post loading handler. */ + @FromAnyThread public void setOnLoadHandler(@Nullable final Consumer onLoadHandler) { this.onLoadHandler = onLoadHandler; } @@ -344,6 +368,7 @@ public void setOnLoadHandler(@Nullable final Consumer onLoadHandler) { /** * @return the post loading handler. */ + @FromAnyThread private @Nullable Consumer getOnLoadHandler() { return onLoadHandler; } @@ -351,6 +376,7 @@ public void setOnLoadHandler(@Nullable final Consumer onLoadHandler) { /** * @return the flag of read only mode. */ + @FromAnyThread private boolean isReadOnly() { return readOnly; } @@ -358,6 +384,7 @@ private boolean isReadOnly() { /** * @return the action tester. */ + @FromAnyThread private @NotNull Predicate> getActionTester() { return actionTester; } @@ -368,6 +395,7 @@ private boolean isReadOnly() { * @param element the element * @return the context menu for the element. */ + @FXThread protected @Nullable ContextMenu getContextMenu(@NotNull final ResourceElement element) { if (isReadOnly()) return null; @@ -414,6 +442,7 @@ private boolean isReadOnly() { * * @param assetFolder the asset folder. */ + @FXThread public void fill(@NotNull final Path assetFolder) { final Consumer onLoadHandler = getOnLoadHandler(); @@ -430,6 +459,7 @@ public void fill(@NotNull final Path assetFolder) { /** * @return the list of expanded elements. */ + @FromAnyThread private @NotNull ConcurrentArray getExpandedElements() { return expandedElements; } @@ -437,6 +467,7 @@ public void fill(@NotNull final Path assetFolder) { /** * @return the list of selected elements. */ + @FromAnyThread private @NotNull ConcurrentArray getSelectedElements() { return selectedElements; } @@ -444,6 +475,7 @@ public void fill(@NotNull final Path assetFolder) { /** * Refresh this tree. */ + @FXThread public void refresh() { final EditorConfig config = EditorConfig.getInstance(); @@ -469,6 +501,7 @@ public void refresh() { /** * Update the list of expanded elements. */ + @FXThread private void updateExpandedElements() { final ConcurrentArray expandedElements = getExpandedElements(); @@ -491,6 +524,7 @@ private void updateExpandedElements() { /** * Update the list of selected elements. */ + @FXThread private void updateSelectedElements() { final ConcurrentArray selectedElements = getSelectedElements(); @@ -511,6 +545,7 @@ private void updateSelectedElements() { /** * Show the process of loading. */ + @FXThread private void showLoading() { setRoot(new TreeItem<>(LoadingResourceElement.getInstance())); } @@ -518,6 +553,7 @@ private void showLoading() { /** * Start the background process of filling. */ + @BackgroundThread private void startBackgroundFill(@NotNull final Path assetFolder) { final ResourceElement rootElement = createFor(assetFolder); @@ -541,6 +577,7 @@ private void startBackgroundFill(@NotNull final Path assetFolder) { /** * Start the background process of loading. */ + @BackgroundThread private void startBackgroundRefresh(@NotNull final Path assetFolder) { final ResourceElement rootElement = createFor(assetFolder); @@ -580,6 +617,7 @@ private void startBackgroundRefresh(@NotNull final Path assetFolder) { /** * Restore selection. */ + @FromAnyThread private void restoreSelection() { EXECUTOR_MANAGER.addFXTask(() -> { @@ -606,6 +644,7 @@ private void restoreSelection() { /** * Fill the node. */ + @FXThread private void fill(@NotNull final TreeItem treeItem) { final ResourceElement element = treeItem.getValue(); @@ -631,6 +670,7 @@ private void fill(@NotNull final TreeItem treeItem) { * * @param file the created file. */ + @FXThread public void notifyCreated(@NotNull final Path file) { final EditorConfig editorConfig = EditorConfig.getInstance(); @@ -664,6 +704,7 @@ public void notifyCreated(@NotNull final Path file) { * * @param file the file */ + @FXThread public void notifyDeleted(@NotNull final Path file) { final ResourceElement element = createFor(file); @@ -683,6 +724,7 @@ public void notifyDeleted(@NotNull final Path file) { * @param prevFile the prev version. * @param newFile the new version. */ + @FXThread public void notifyMoved(@NotNull final Path prevFile, @NotNull final Path newFile) { final ResourceElement prevElement = createFor(prevFile); @@ -710,6 +752,7 @@ public void notifyMoved(@NotNull final Path prevFile, @NotNull final Path newFil FXCollections.sort(newParentChildren, ITEM_COMPARATOR); } + @FXThread private void fillChildren(@NotNull final Path prevFile, @NotNull final Path newFile, @NotNull final Array> children) { for (final TreeItem child : children) { @@ -729,6 +772,7 @@ private void fillChildren(@NotNull final Path prevFile, @NotNull final Path newF * @param prevFile the prev version. * @param newFile the new version. */ + @FXThread public void notifyRenamed(@NotNull final Path prevFile, @NotNull final Path newFile) { final ResourceElement prevElement = createFor(prevFile); @@ -746,6 +790,7 @@ public void notifyRenamed(@NotNull final Path prevFile, @NotNull final Path newF /** * Handle hotkeys. */ + @FXThread private void processKey(@NotNull final KeyEvent event) { if (isReadOnly()) return; @@ -819,6 +864,7 @@ private void processKey(@NotNull final KeyEvent event) { * * @return the open resource function. */ + @FromAnyThread protected @Nullable Consumer getOpenFunction() { return openFunction; } @@ -826,6 +872,7 @@ private void processKey(@NotNull final KeyEvent event) { /** * Cleanup the tree. */ + @FXThread private void cleanup(@NotNull final TreeItem treeItem) { final ResourceElement element = treeItem.getValue(); @@ -850,6 +897,7 @@ private void cleanup(@NotNull final TreeItem treeItem) { * @param treeItem the tree item * @param needSelect the need select */ + @FXThread public void expandTo(@NotNull final TreeItem treeItem, final boolean needSelect) { TreeItem parent = treeItem; @@ -869,6 +917,7 @@ public void expandTo(@NotNull final TreeItem treeItem, final bo * * @param file the file */ + @FXThread public void markExpand(@NotNull final Path file) { final ResourceElement element = createFor(file); @@ -883,6 +932,7 @@ public void markExpand(@NotNull final Path file) { * * @param onlyFolders true if need to show only folders. */ + @FromAnyThread public void setOnlyFolders(final boolean onlyFolders) { this.onlyFolders = onlyFolders; } @@ -890,6 +940,7 @@ public void setOnlyFolders(final boolean onlyFolders) { /** * @return true if need to show only folders. */ + @FromAnyThread public boolean isOnlyFolders() { return onlyFolders; } @@ -952,6 +1003,7 @@ public void expandTo(@NotNull final Path file, final boolean needSelect) { } } + @FromAnyThread private void scrollToAndSelect(@NotNull final TreeItem treeItem) { EXECUTOR_MANAGER.addFXTask(() -> { final MultipleSelectionModel> selectionModel = getSelectionModel(); diff --git a/src/main/java/com/ss/editor/ui/component/asset/tree/ResourceTreeCell.java b/src/main/java/com/ss/editor/ui/component/asset/tree/ResourceTreeCell.java index 364311f2..b6fbb54c 100644 --- a/src/main/java/com/ss/editor/ui/component/asset/tree/ResourceTreeCell.java +++ b/src/main/java/com/ss/editor/ui/component/asset/tree/ResourceTreeCell.java @@ -2,6 +2,7 @@ import static com.ss.editor.manager.FileIconManager.DEFAULT_FILE_ICON_SIZE; import static java.util.Collections.singletonList; +import com.ss.editor.annotation.FXThread; import com.ss.editor.manager.FileIconManager; import com.ss.editor.ui.FXConstants; import com.ss.editor.ui.component.asset.tree.resource.FolderResourceElement; @@ -27,6 +28,9 @@ */ public class ResourceTreeCell extends TreeCell { + /** + * The file icon manager. + */ @NotNull private static final FileIconManager ICON_MANAGER = FileIconManager.getInstance(); @@ -46,25 +50,31 @@ public class ResourceTreeCell extends TreeCell { * Instantiates a new Resource tree cell. */ protected ResourceTreeCell() { - setOnMouseClicked(this::processClick); - setOnDragDetected(this::startDrag); - setOnDragDone(this::stopDrag); + setOnMouseClicked(this::handleMouseClickedEvent); + setOnDragDetected(this::handleStartDragEvent); + setOnDragDone(this::handleStopDragEvent); this.icon = new ImageView(); this.tooltip = new Tooltip(); } /** - * Handle stopping dragging. + * Handle stop drag events. + * + * @param event the stop drag event. */ - private void stopDrag(@NotNull final DragEvent event) { + @FXThread + private void handleStopDragEvent(@NotNull final DragEvent event) { setCursor(Cursor.DEFAULT); event.consume(); } /** - * Handle starting dragging. + * Handle start drag events. + * + * @param mouseEvent the mouse event. */ - private void startDrag(@NotNull final MouseEvent mouseEvent) { + @FXThread + private void handleStartDragEvent(@NotNull final MouseEvent mouseEvent) { startFullDrag(); final ResourceElement item = getItem(); @@ -84,9 +94,12 @@ private void startDrag(@NotNull final MouseEvent mouseEvent) { } /** - * Handle a click. + * Handle mouse clicked events. + * + * @param event the mouse clicked event. */ - private void processClick(@NotNull final MouseEvent event) { + @FXThread + private void handleMouseClickedEvent(@NotNull final MouseEvent event) { final ResourceElement item = getItem(); if (item == null) return; @@ -106,6 +119,7 @@ private void processClick(@NotNull final MouseEvent event) { } @Override + @FXThread protected void updateItem(@Nullable final ResourceElement item, boolean empty) { super.updateItem(item, empty); @@ -123,14 +137,16 @@ protected void updateItem(@Nullable final ResourceElement item, boolean empty) { final Path file = item.getFile(); final Path fileName = file.getFileName(); + final boolean folder = item instanceof FolderResourceElement; - icon.setImage(ICON_MANAGER.getIcon(file, DEFAULT_FILE_ICON_SIZE)); + icon.setImage(ICON_MANAGER.getIcon(file, folder, true, DEFAULT_FILE_ICON_SIZE)); setText(fileName.toString()); setGraphic(icon); createToolTip(); } + @FXThread private @NotNull ProgressIndicator createIndicator() { final ProgressIndicator indicator = new ProgressIndicator(); indicator.setMaxHeight(FXConstants.RESOURCE_TREE_CELL_HEIGHT - 2); @@ -138,12 +154,14 @@ protected void updateItem(@Nullable final ResourceElement item, boolean empty) { return indicator; } + @FXThread private void removeToolTip() { if (tooltip == null) return; Tooltip.uninstall(this, tooltip); tooltip = null; } + @FXThread private void createToolTip() { tooltip = getItem().createToolTip(); if (tooltip == null) return; diff --git a/src/main/java/com/ss/editor/ui/component/asset/tree/context/menu/action/OpenFileInExplorerAction.java b/src/main/java/com/ss/editor/ui/component/asset/tree/context/menu/action/OpenFileInExplorerAction.java index d5b13763..8fcf29a6 100644 --- a/src/main/java/com/ss/editor/ui/component/asset/tree/context/menu/action/OpenFileInExplorerAction.java +++ b/src/main/java/com/ss/editor/ui/component/asset/tree/context/menu/action/OpenFileInExplorerAction.java @@ -1,9 +1,12 @@ package com.ss.editor.ui.component.asset.tree.context.menu.action; +import com.ss.editor.FileExtensions; import com.ss.editor.Messages; +import com.ss.editor.manager.ResourceManager; import com.ss.editor.ui.Icons; import com.ss.editor.ui.component.asset.tree.resource.ResourceElement; -import com.ss.editor.util.EditorUtil; +import com.ss.editor.ui.util.UIUtils; +import com.ss.rlib.util.array.Array; import javafx.event.ActionEvent; import javafx.scene.image.Image; import org.jetbrains.annotations.NotNull; @@ -33,6 +36,12 @@ public OpenFileInExplorerAction(@NotNull final ResourceElement element) { @Override protected void execute(@Nullable final ActionEvent event) { super.execute(event); - EditorUtil.openFileInSystemExplorer(getElement().getFile()); + + final ResourceManager resourceManager = ResourceManager.getInstance(); + final Array shaderNodes = resourceManager.getAvailableResources(FileExtensions.JME_SHADER_NODE); + + UIUtils.openResourceAssetDialog(s -> System.out.println(s), shaderNodes); + + //EditorUtil.openFileInSystemExplorer(getElement().getFile()); } } diff --git a/src/main/java/com/ss/editor/ui/component/asset/tree/context/menu/filler/AssetTreeMultiContextMenuFiller.java b/src/main/java/com/ss/editor/ui/component/asset/tree/context/menu/filler/AssetTreeMultiContextMenuFiller.java index 44b36744..d97310b6 100644 --- a/src/main/java/com/ss/editor/ui/component/asset/tree/context/menu/filler/AssetTreeMultiContextMenuFiller.java +++ b/src/main/java/com/ss/editor/ui/component/asset/tree/context/menu/filler/AssetTreeMultiContextMenuFiller.java @@ -1,5 +1,6 @@ package com.ss.editor.ui.component.asset.tree.context.menu.filler; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.component.asset.tree.resource.ResourceElement; import com.ss.rlib.util.array.Array; import javafx.scene.control.MenuItem; @@ -23,6 +24,7 @@ public interface AssetTreeMultiContextMenuFiller { * @param items the container of items of a context menu. * @param actionTester the action tester. */ + @FXThread void fill(@NotNull final Array elements, @NotNull final List items, @NotNull final Predicate> actionTester); } \ No newline at end of file diff --git a/src/main/java/com/ss/editor/ui/component/asset/tree/context/menu/filler/AssetTreeSingleContextMenuFiller.java b/src/main/java/com/ss/editor/ui/component/asset/tree/context/menu/filler/AssetTreeSingleContextMenuFiller.java index 0c2e8c89..8f477f6d 100644 --- a/src/main/java/com/ss/editor/ui/component/asset/tree/context/menu/filler/AssetTreeSingleContextMenuFiller.java +++ b/src/main/java/com/ss/editor/ui/component/asset/tree/context/menu/filler/AssetTreeSingleContextMenuFiller.java @@ -1,5 +1,6 @@ package com.ss.editor.ui.component.asset.tree.context.menu.filler; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.component.asset.tree.resource.ResourceElement; import javafx.scene.control.MenuItem; import org.jetbrains.annotations.NotNull; @@ -22,6 +23,7 @@ public interface AssetTreeSingleContextMenuFiller { * @param items the container of items of a context menu. * @param actionTester the action tester. */ + @FXThread void fill(@NotNull final ResourceElement element, @NotNull final List items, @NotNull final Predicate> actionTester); } \ No newline at end of file diff --git a/src/main/java/com/ss/editor/ui/component/asset/tree/context/menu/filler/impl/FileAssetTreeSingleContextMenuFiller.java b/src/main/java/com/ss/editor/ui/component/asset/tree/context/menu/filler/impl/FileAssetTreeSingleContextMenuFiller.java index 65df836a..4c964007 100644 --- a/src/main/java/com/ss/editor/ui/component/asset/tree/context/menu/filler/impl/FileAssetTreeSingleContextMenuFiller.java +++ b/src/main/java/com/ss/editor/ui/component/asset/tree/context/menu/filler/impl/FileAssetTreeSingleContextMenuFiller.java @@ -1,5 +1,6 @@ package com.ss.editor.ui.component.asset.tree.context.menu.filler.impl; +import com.ss.editor.annotation.FXThread; import com.ss.editor.file.converter.FileConverterDescription; import com.ss.editor.file.converter.FileConverterRegistry; import com.ss.editor.ui.component.asset.tree.context.menu.action.ConvertFileAction; @@ -30,6 +31,7 @@ public class FileAssetTreeSingleContextMenuFiller implements AssetTreeSingleCont private static final FileConverterRegistry FILE_CONVERTER_REGISTRY = FileConverterRegistry.getInstance(); @Override + @FXThread public void fill(@NotNull final ResourceElement element, @NotNull final List items, @NotNull final Predicate> actionTester) { @@ -60,6 +62,7 @@ public void fill(@NotNull final ResourceElement element, @NotNull final List elements, @NotNull final List items, @NotNull final Predicate> actionTester) { } diff --git a/src/main/java/com/ss/editor/ui/component/asset/tree/context/menu/filler/impl/ResourceAssetTreeSingleContextMenuFiller.java b/src/main/java/com/ss/editor/ui/component/asset/tree/context/menu/filler/impl/ResourceAssetTreeSingleContextMenuFiller.java index 08a048fc..ffbc10d1 100644 --- a/src/main/java/com/ss/editor/ui/component/asset/tree/context/menu/filler/impl/ResourceAssetTreeSingleContextMenuFiller.java +++ b/src/main/java/com/ss/editor/ui/component/asset/tree/context/menu/filler/impl/ResourceAssetTreeSingleContextMenuFiller.java @@ -1,6 +1,7 @@ package com.ss.editor.ui.component.asset.tree.context.menu.filler.impl; import static com.ss.editor.util.EditorUtil.hasFileInClipboard; +import com.ss.editor.annotation.FXThread; import com.ss.editor.config.EditorConfig; import com.ss.editor.ui.component.asset.tree.context.menu.action.*; import com.ss.editor.ui.component.asset.tree.context.menu.filler.AssetTreeMultiContextMenuFiller; @@ -26,6 +27,7 @@ public class ResourceAssetTreeSingleContextMenuFiller implements AssetTreeSingle AssetTreeMultiContextMenuFiller { @Override + @FXThread public void fill(@NotNull final ResourceElement element, @NotNull final List items, @NotNull final Predicate> actionTester) { @@ -57,6 +59,7 @@ public void fill(@NotNull final ResourceElement element, @NotNull final List elements, @NotNull final List items, @NotNull final Predicate> actionTester) { diff --git a/src/main/java/com/ss/editor/ui/component/asset/tree/resource/FileResourceElement.java b/src/main/java/com/ss/editor/ui/component/asset/tree/resource/FileResourceElement.java index 41054f3f..21206a5c 100644 --- a/src/main/java/com/ss/editor/ui/component/asset/tree/resource/FileResourceElement.java +++ b/src/main/java/com/ss/editor/ui/component/asset/tree/resource/FileResourceElement.java @@ -11,12 +11,7 @@ */ public class FileResourceElement extends ResourceElement { - /** - * Instantiates a new File element. - * - * @param file the file - */ - FileResourceElement(@NotNull final Path file) { + public FileResourceElement(@NotNull final Path file) { super(file); } } diff --git a/src/main/java/com/ss/editor/ui/component/asset/tree/resource/FolderResourceElement.java b/src/main/java/com/ss/editor/ui/component/asset/tree/resource/FolderResourceElement.java index 5471de02..c2b394e7 100644 --- a/src/main/java/com/ss/editor/ui/component/asset/tree/resource/FolderResourceElement.java +++ b/src/main/java/com/ss/editor/ui/component/asset/tree/resource/FolderResourceElement.java @@ -1,10 +1,12 @@ package com.ss.editor.ui.component.asset.tree.resource; import static com.ss.editor.ui.component.asset.tree.resource.ResourceElementFactory.createFor; +import com.ss.editor.annotation.FromAnyThread; import com.ss.rlib.util.FileUtils; import com.ss.rlib.util.array.Array; import com.ss.rlib.util.array.ArrayFactory; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.nio.file.AccessDeniedException; @@ -19,17 +21,13 @@ */ public class FolderResourceElement extends ResourceElement { - /** - * Instantiates a new Folder element. - * - * @param file the file - */ - FolderResourceElement(@NotNull final Path file) { + public FolderResourceElement(@NotNull final Path file) { super(file); } @Override - public Array getChildren(@NotNull final Array extensionFilter, final boolean onlyFolders) { + @FromAnyThread + public @Nullable Array getChildren(@NotNull final Array extensionFilter, final boolean onlyFolders) { if (!Files.isDirectory(file)) return null; final Array elements = ArrayFactory.newArray(ResourceElement.class); @@ -63,6 +61,7 @@ public Array getChildren(@NotNull final Array extension } @Override + @FromAnyThread public boolean hasChildren(@NotNull final Array extensionFilter, final boolean onlyFolders) { if (!Files.isDirectory(file)) return false; diff --git a/src/main/java/com/ss/editor/ui/component/asset/tree/resource/ImageResourceElement.java b/src/main/java/com/ss/editor/ui/component/asset/tree/resource/ImageResourceElement.java index 1d0a041f..bfb72f96 100644 --- a/src/main/java/com/ss/editor/ui/component/asset/tree/resource/ImageResourceElement.java +++ b/src/main/java/com/ss/editor/ui/component/asset/tree/resource/ImageResourceElement.java @@ -1,5 +1,6 @@ package com.ss.editor.ui.component.asset.tree.resource; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.tooltip.ImagePreview; import javafx.scene.control.Tooltip; import org.jetbrains.annotations.NotNull; @@ -14,16 +15,12 @@ */ public class ImageResourceElement extends FileResourceElement { - /** - * Instantiates a new ImageResourceElement. - * - * @param file the file - */ - ImageResourceElement(@NotNull final Path file) { + public ImageResourceElement(@NotNull final Path file) { super(file); } @Override + @FXThread public @Nullable Tooltip createToolTip() { return new ImagePreview(getFile()); } diff --git a/src/main/java/com/ss/editor/ui/component/asset/tree/resource/ResourceElement.java b/src/main/java/com/ss/editor/ui/component/asset/tree/resource/ResourceElement.java index 974cab5e..a8d14148 100644 --- a/src/main/java/com/ss/editor/ui/component/asset/tree/resource/ResourceElement.java +++ b/src/main/java/com/ss/editor/ui/component/asset/tree/resource/ResourceElement.java @@ -1,5 +1,7 @@ package com.ss.editor.ui.component.asset.tree.resource; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.rlib.logging.Logger; import com.ss.rlib.logging.LoggerManager; import com.ss.rlib.util.array.Array; @@ -17,7 +19,7 @@ public abstract class ResourceElement implements Comparable { /** - * The constant LOGGER. + * The logger. */ @NotNull protected static final Logger LOGGER = LoggerManager.getLogger(ResourceElement.class); @@ -28,11 +30,6 @@ public abstract class ResourceElement implements Comparable { @NotNull protected final Path file; - /** - * Instantiates a new Resource element. - * - * @param file the file - */ public ResourceElement(@NotNull final Path file) { this.file = file; } @@ -42,8 +39,8 @@ public ResourceElement(@NotNull final Path file) { * * @return the tooltip. */ - @Nullable - public Tooltip createToolTip() { + @FXThread + public @Nullable Tooltip createToolTip() { return null; } @@ -52,8 +49,8 @@ public Tooltip createToolTip() { * * @return the reference to the file. */ - @NotNull - public Path getFile() { + @FromAnyThread + public @NotNull Path getFile() { return file; } @@ -64,7 +61,8 @@ public Path getFile() { * @param onlyFolders the only folders * @return list of children resource elements. */ - public Array getChildren(@NotNull final Array extensionFilter, final boolean onlyFolders) { + @FromAnyThread + public @Nullable Array getChildren(@NotNull final Array extensionFilter, final boolean onlyFolders) { return null; } @@ -75,6 +73,7 @@ public Array getChildren(@NotNull final Array extension * @param onlyFolders the only folders * @return true if this element has children. */ + @FromAnyThread public boolean hasChildren(@NotNull final Array extensionFilter, final boolean onlyFolders) { return false; } diff --git a/src/main/java/com/ss/editor/ui/component/asset/tree/resource/ResourceElementFactory.java b/src/main/java/com/ss/editor/ui/component/asset/tree/resource/ResourceElementFactory.java index 43f71296..a2e00582 100644 --- a/src/main/java/com/ss/editor/ui/component/asset/tree/resource/ResourceElementFactory.java +++ b/src/main/java/com/ss/editor/ui/component/asset/tree/resource/ResourceElementFactory.java @@ -1,5 +1,6 @@ package com.ss.editor.ui.component.asset.tree.resource; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.manager.JavaFXImageManager; import org.jetbrains.annotations.NotNull; @@ -14,13 +15,13 @@ public class ResourceElementFactory { /** - * Create for resource element. + * Create a resource element for the file. * - * @param file the file - * @return the resource element + * @param file the file. + * @return the created resource element. */ - @NotNull - public static ResourceElement createFor(@NotNull final Path file) { + @FromAnyThread + public static @NotNull ResourceElement createFor(@NotNull final Path file) { if (Files.isDirectory(file)) { return new FolderResourceElement(file); } else if (JavaFXImageManager.isImage(file)) { diff --git a/src/main/java/com/ss/editor/ui/component/creator/impl/DefaultSceneCreator.java b/src/main/java/com/ss/editor/ui/component/creator/impl/DefaultSceneCreator.java index 1a817140..03c4a2da 100644 --- a/src/main/java/com/ss/editor/ui/component/creator/impl/DefaultSceneCreator.java +++ b/src/main/java/com/ss/editor/ui/component/creator/impl/DefaultSceneCreator.java @@ -3,6 +3,7 @@ import com.jme3.renderer.queue.RenderQueue; import com.jme3.shadow.EdgeFilteringMode; import com.ss.editor.Messages; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.extension.scene.SceneNode; import com.ss.editor.extension.scene.app.state.impl.EditableLightingSceneAppState; import com.ss.editor.extension.scene.app.state.impl.EditableSkySceneAppState; @@ -29,14 +30,14 @@ public class DefaultSceneCreator extends EmptySceneCreator { DESCRIPTION.setConstructor(DefaultSceneCreator::new); } - @NotNull @Override - protected String getTitleText() { + @FromAnyThread + protected @NotNull String getTitleText() { return Messages.DEFAULT_SCENE_CREATOR_TITLE; } - @NotNull @Override + @NotNull protected SceneNode createScene() { final EditableLightingStateShadowFilter shadowFilter = new EditableLightingStateShadowFilter(); diff --git a/src/main/java/com/ss/editor/ui/component/creator/impl/EmptyFileCreator.java b/src/main/java/com/ss/editor/ui/component/creator/impl/EmptyFileCreator.java index e6ac6d18..a6994462 100644 --- a/src/main/java/com/ss/editor/ui/component/creator/impl/EmptyFileCreator.java +++ b/src/main/java/com/ss/editor/ui/component/creator/impl/EmptyFileCreator.java @@ -1,6 +1,7 @@ package com.ss.editor.ui.component.creator.impl; import com.ss.editor.Messages; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.component.creator.FileCreatorDescription; import com.ss.rlib.util.StringUtils; import org.jetbrains.annotations.NotNull; @@ -23,15 +24,16 @@ public class EmptyFileCreator extends AbstractFileCreator { DESCRIPTION.setConstructor(EmptyFileCreator::new); } - @NotNull @Override - protected String getTitleText() { + @FromAnyThread + protected @NotNull String getTitleText() { return Messages.EMPTY_FILE_CREATOR_TITLE; } - @NotNull + @Override - protected String getFileExtension() { + @FromAnyThread + protected @NotNull String getFileExtension() { return StringUtils.EMPTY; } } diff --git a/src/main/java/com/ss/editor/ui/component/creator/impl/EmptyModelCreator.java b/src/main/java/com/ss/editor/ui/component/creator/impl/EmptyModelCreator.java index abac50e2..d216070a 100644 --- a/src/main/java/com/ss/editor/ui/component/creator/impl/EmptyModelCreator.java +++ b/src/main/java/com/ss/editor/ui/component/creator/impl/EmptyModelCreator.java @@ -8,6 +8,7 @@ import com.ss.editor.FileExtensions; import com.ss.editor.Messages; import com.ss.editor.annotation.BackgroundThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.component.creator.FileCreatorDescription; import org.jetbrains.annotations.NotNull; @@ -34,15 +35,15 @@ public class EmptyModelCreator extends AbstractFileCreator { DESCRIPTION.setConstructor(EmptyModelCreator::new); } - @NotNull @Override - protected String getTitleText() { + @FromAnyThread + protected @NotNull String getTitleText() { return Messages.EMPTY_MODEL_CREATOR_TITLE; } - @NotNull @Override - protected String getFileExtension() { + @FromAnyThread + protected @NotNull String getFileExtension() { return FileExtensions.JME_OBJECT; } diff --git a/src/main/java/com/ss/editor/ui/component/creator/impl/EmptySceneCreator.java b/src/main/java/com/ss/editor/ui/component/creator/impl/EmptySceneCreator.java index 90e012a5..646fec83 100644 --- a/src/main/java/com/ss/editor/ui/component/creator/impl/EmptySceneCreator.java +++ b/src/main/java/com/ss/editor/ui/component/creator/impl/EmptySceneCreator.java @@ -7,6 +7,8 @@ import com.ss.editor.FileExtensions; import com.ss.editor.Messages; import com.ss.editor.annotation.BackgroundThread; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.extension.scene.SceneLayer; import com.ss.editor.extension.scene.SceneNode; import com.ss.editor.ui.component.creator.FileCreatorDescription; @@ -35,15 +37,15 @@ public class EmptySceneCreator extends AbstractFileCreator { DESCRIPTION.setConstructor(EmptySceneCreator::new); } - @NotNull @Override - protected String getTitleText() { + @FromAnyThread + protected @NotNull String getTitleText() { return Messages.EMPTY_SCENE_CREATOR_TITLE; } - @NotNull @Override - protected String getFileExtension() { + @FromAnyThread + protected @NotNull String getFileExtension() { return FileExtensions.JME_SCENE; } @@ -65,8 +67,8 @@ protected void writeData(@NotNull final Path resultFile) throws IOException { * * @return the scene node */ - @NotNull - protected SceneNode createScene() { + @FXThread + protected @NotNull SceneNode createScene() { final SceneNode newNode = new SceneNode(); newNode.addLayer(new SceneLayer("Default", true)); newNode.addLayer(new SceneLayer("TransparentFX", true)); diff --git a/src/main/java/com/ss/editor/ui/component/creator/impl/FolderCreator.java b/src/main/java/com/ss/editor/ui/component/creator/impl/FolderCreator.java index 16a70cf5..3a0c4634 100644 --- a/src/main/java/com/ss/editor/ui/component/creator/impl/FolderCreator.java +++ b/src/main/java/com/ss/editor/ui/component/creator/impl/FolderCreator.java @@ -2,6 +2,7 @@ import static com.ss.rlib.util.ObjectUtils.notNull; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.component.creator.FileCreatorDescription; import com.ss.editor.util.EditorUtil; @@ -30,29 +31,26 @@ public class FolderCreator extends AbstractFileCreator { DESCRIPTION.setConstructor(FolderCreator::new); } - private FolderCreator() { - } - - @NotNull @Override - protected String getTitleText() { + @FromAnyThread + protected @NotNull String getTitleText() { return Messages.FOLDER_CREATOR_TITLE; } - @NotNull @Override - protected String getFileExtension() { + @FromAnyThread + protected @NotNull String getFileExtension() { return StringUtils.EMPTY; } - @NotNull @Override @FromAnyThread - protected String getFileNameLabelText() { + protected @NotNull String getFileNameLabelText() { return Messages.FOLDER_CREATOR_FILE_NAME_LABEL; } @Override + @FXThread protected void processOk() { super.hide(); diff --git a/src/main/java/com/ss/editor/ui/component/creator/impl/material/MaterialFileCreator.java b/src/main/java/com/ss/editor/ui/component/creator/impl/material/MaterialFileCreator.java index c72cfe70..022df0d2 100644 --- a/src/main/java/com/ss/editor/ui/component/creator/impl/material/MaterialFileCreator.java +++ b/src/main/java/com/ss/editor/ui/component/creator/impl/material/MaterialFileCreator.java @@ -5,6 +5,7 @@ import static java.nio.file.StandardOpenOption.*; import com.jme3.asset.AssetManager; import com.jme3.material.Material; +import com.ss.editor.FileExtensions; import com.ss.editor.Messages; import com.ss.editor.annotation.BackgroundThread; import com.ss.editor.annotation.FXThread; @@ -63,24 +64,23 @@ public class MaterialFileCreator extends GenericFileCreator { @Nullable private Array definitions; - private MaterialFileCreator() { - super(); - } - @Override + @FromAnyThread protected @NotNull String getTitleText() { return Messages.MATERIAL_FILE_CREATOR_TITLE; } @Override + @FromAnyThread protected @NotNull String getFileExtension() { return JME_MATERIAL; } @Override + @FromAnyThread protected @NotNull Array getPropertyDefinitions() { - definitions = RESOURCE_MANAGER.getAvailableMaterialDefinitions(); + definitions = RESOURCE_MANAGER.getAvailableResources(FileExtensions.JME_MATERIAL_DEFINITION); final String def; diff --git a/src/main/java/com/ss/editor/ui/component/creator/impl/material/definition/MaterialDefinitionFileCreator.java b/src/main/java/com/ss/editor/ui/component/creator/impl/material/definition/MaterialDefinitionFileCreator.java index 37c50c91..f9909794 100644 --- a/src/main/java/com/ss/editor/ui/component/creator/impl/material/definition/MaterialDefinitionFileCreator.java +++ b/src/main/java/com/ss/editor/ui/component/creator/impl/material/definition/MaterialDefinitionFileCreator.java @@ -11,6 +11,8 @@ import com.jme3.renderer.Renderer; import com.ss.editor.FileExtensions; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.config.EditorConfig; import com.ss.editor.extension.property.EditablePropertyType; import com.ss.editor.plugin.api.file.creator.GenericFileCreator; @@ -86,23 +88,20 @@ public class MaterialDefinitionFileCreator extends GenericFileCreator { VERT_TEMPLATE = FileUtils.read(vertResource); } - public MaterialDefinitionFileCreator() { - super(); - } - - @NotNull @Override - protected String getTitleText() { + @FromAnyThread + protected @NotNull String getTitleText() { return Messages.MATERIAL_DEFINITION_FILE_CREATOR_TITLE; } - @NotNull @Override - protected String getFileExtension() { + @FromAnyThread + protected @NotNull String getFileExtension() { return JME_MATERIAL_DEFINITION; } @Override + @FXThread protected boolean validate(@NotNull final VarTable vars) { final String glslVersion = vars.get(PROP_GLSL_VERSION, String.class, StringUtils.EMPTY); @@ -114,9 +113,9 @@ protected boolean validate(@NotNull final VarTable vars) { return super.validate(vars); } - @NotNull @Override - protected Array getPropertyDefinitions() { + @FromAnyThread + protected @NotNull Array getPropertyDefinitions() { final Array result = ArrayFactory.newArray(PropertyDefinition.class); result.add(new PropertyDefinition(EditablePropertyType.STRING_FROM_LIST, @@ -126,6 +125,7 @@ protected Array getPropertyDefinitions() { } @Override + @FXThread protected void processOk() { super.hide(); diff --git a/src/main/java/com/ss/editor/ui/component/creator/impl/texture/SingleColorTextureFileCreator.java b/src/main/java/com/ss/editor/ui/component/creator/impl/texture/SingleColorTextureFileCreator.java index 445fdd33..44c0a587 100644 --- a/src/main/java/com/ss/editor/ui/component/creator/impl/texture/SingleColorTextureFileCreator.java +++ b/src/main/java/com/ss/editor/ui/component/creator/impl/texture/SingleColorTextureFileCreator.java @@ -71,10 +71,6 @@ public class SingleColorTextureFileCreator extends GenericFileCreator { @Nullable private ImageView imageView; - public SingleColorTextureFileCreator() { - super(); - } - @Override @FXThread protected void createPreview(@NotNull final BorderPane container) { @@ -86,6 +82,7 @@ protected void createPreview(@NotNull final BorderPane container) { /** * @return the image view to show preview of texture. */ + @FXThread private @NotNull ImageView getImageView() { return notNull(imageView); } @@ -97,11 +94,13 @@ protected boolean needPreview() { } @Override + @FromAnyThread protected @NotNull String getTitleText() { return Messages.SINGLE_COLOR_TEXTURE_FILE_CREATOR_TITLE; } @Override + @FromAnyThread protected @NotNull String getFileExtension() { return FileExtensions.IMAGE_PNG; } diff --git a/src/main/java/com/ss/editor/ui/component/editor/impl/material/MaterialFileEditor.java b/src/main/java/com/ss/editor/ui/component/editor/impl/material/MaterialFileEditor.java index 33fedc2b..8a56ad4d 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/impl/material/MaterialFileEditor.java +++ b/src/main/java/com/ss/editor/ui/component/editor/impl/material/MaterialFileEditor.java @@ -255,7 +255,7 @@ private void reload(@NotNull final Material material) { final ComboBox materialDefinitionBox = getMaterialDefinitionBox(); final ObservableList items = materialDefinitionBox.getItems(); items.clear(); - items.addAll(RESOURCE_MANAGER.getAvailableMaterialDefinitions()); + items.addAll(RESOURCE_MANAGER.getAvailableResources(FileExtensions.JME_MATERIAL_DEFINITION)); final MaterialDef materialDef = material.getMaterialDef(); materialDefinitionBox.getSelectionModel().select(materialDef.getAssetName()); diff --git a/src/main/java/com/ss/editor/ui/component/virtual/tree/VirtualResourceTree.java b/src/main/java/com/ss/editor/ui/component/virtual/tree/VirtualResourceTree.java new file mode 100644 index 00000000..7f927914 --- /dev/null +++ b/src/main/java/com/ss/editor/ui/component/virtual/tree/VirtualResourceTree.java @@ -0,0 +1,237 @@ +package com.ss.editor.ui.component.virtual.tree; + +import static com.ss.rlib.util.ObjectUtils.notNull; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; +import com.ss.editor.ui.FXConstants; +import com.ss.editor.ui.component.virtual.tree.resource.FolderVirtualResourceElement; +import com.ss.editor.ui.component.virtual.tree.resource.VirtualResourceElement; +import com.ss.editor.ui.util.UIUtils; +import com.ss.rlib.util.FileUtils; +import com.ss.rlib.util.StringUtils; +import com.ss.rlib.util.array.Array; +import com.ss.rlib.util.array.ArrayComparator; +import javafx.collections.ObservableList; +import javafx.scene.control.SelectionMode; +import javafx.scene.control.TreeItem; +import javafx.scene.control.TreeView; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * THe implementation of a tree with resources of an asset folder. + * + * @author JavaSaBr + */ +public class VirtualResourceTree extends TreeView> { + + @NotNull + private static final ArrayComparator> NAME_COMPARATOR = (first, second) -> { + + final int firstLevel = getLevel(first); + final int secondLevel = getLevel(second); + + if (firstLevel != secondLevel) return firstLevel - secondLevel; + + final String firstName = notNull(first).getName(); + final String secondName = notNull(second).getName(); + + return StringUtils.compareIgnoreCase(firstName, secondName); + }; + + private static int getLevel(@Nullable final VirtualResourceElement element) { + if (element instanceof FolderVirtualResourceElement) return 1; + return 2; + } + + /** + * The target objects type. + */ + @NotNull + private final Class objectsType; + + /** + * The open resource function. + */ + @Nullable + private Consumer> openFunction; + + /** + * The function to get an asset path of the resource element. + */ + @Nullable + private Function pathFunction; + + /** + * The function to get a name of the resource element. + */ + @Nullable + private Function nameFunction; + + public VirtualResourceTree(@NotNull final Class objectsType) { + this.objectsType = objectsType; + + getSelectionModel().setSelectionMode(SelectionMode.SINGLE); + setFixedCellSize(FXConstants.RESOURCE_TREE_CELL_HEIGHT); + setCellFactory(param -> new VirtualResourceTreeCell()); + setShowRoot(false); + setFocusTraversable(true); + } + + /** + * The type of target objects. + * + * @return the target type. + */ + @FromAnyThread + private @NotNull Class getObjectsType() { + return objectsType; + } + + /** + * Get the function to get an asset path of the resource element. + * + * @return the function to get an asset path of the resource element. + */ + @FromAnyThread + private @Nullable Function getPathFunction() { + return pathFunction; + } + + /** + * Set the function to get an asset path of the resource element. + * + * @param pathFunction the function to get an asset path of the resource element. + */ + @FromAnyThread + public void setPathFunction(final Function pathFunction) { + this.pathFunction = pathFunction; + } + + /** + * Get the function to get a name of the resource element. + * + * @return the function to get a name of the resource element. + */ + @FromAnyThread + private @Nullable Function getNameFunction() { + return nameFunction; + } + + /** + * Get the path of the object. + * + * @param object the object. + * @return the path. + */ + @FromAnyThread + public @NotNull String getPath(@NotNull final Object object) { + + if (object instanceof String) { + return object.toString(); + } else if (!getObjectsType().isInstance(object)) { + throw new RuntimeException("Unknown type of the object " + object); + } + + final Function pathFunction = getPathFunction(); + + if (pathFunction != null) { + return pathFunction.apply(getObjectsType().cast(object)); + } + + throw new RuntimeException("Unknown type of the object " + object); + } + + /** + * Get the name of the object. + * + * @param object the object. + * @return the name. + */ + @FromAnyThread + public @NotNull String getName(@NotNull final Object object) { + + if (object instanceof String) { + final String path = object.toString(); + return FileUtils.getName(path, '/'); + } else if (!getObjectsType().isInstance(object)) { + throw new RuntimeException("Unknown type of the object " + object); + } + + final Function nameFunction = getNameFunction(); + + if (nameFunction != null) { + return nameFunction.apply(getObjectsType().cast(object)); + } + + throw new RuntimeException("Unknown type of the object " + object); + } + + /** + * Fill the tree by the root element. + * + * @param element the root element. + */ + @FXThread + public void fill(@NotNull final VirtualResourceElement element) { + + final TreeItem> newRoot = new TreeItem<>(element); + newRoot.setExpanded(true); + + fill(newRoot); + setRoot(newRoot); + } + + /** + * Expand all nodes. + */ + @FXThread + public void expandAll() { + UIUtils.visit(getRoot(), item -> item.setExpanded(true)); + } + + /** + * Fill the tree item. + * + * @param item the tree item. + */ + @FXThread + private void fill(@NotNull final TreeItem> item) { + + final VirtualResourceElement element = item.getValue(); + if(!element.hasChildren()) { + return; + } + + final ObservableList>> items = item.getChildren(); + + final Array> children = element.getChildren(); + children.sort(NAME_COMPARATOR); + children.forEach(child -> items.add(new TreeItem<>(child))); + + items.forEach(this::fill); + } + + /** + * Get open function. + * + * @return the open resource function. + */ + @FromAnyThread + protected @Nullable Consumer> getOpenFunction() { + return openFunction; + } + + /** + * Set open function. + * + * @param openFunction the open resource function. + */ + @FromAnyThread + public void setOpenFunction(@Nullable final Consumer> openFunction) { + this.openFunction = openFunction; + } +} diff --git a/src/main/java/com/ss/editor/ui/component/virtual/tree/VirtualResourceTreeCell.java b/src/main/java/com/ss/editor/ui/component/virtual/tree/VirtualResourceTreeCell.java new file mode 100644 index 00000000..8384775a --- /dev/null +++ b/src/main/java/com/ss/editor/ui/component/virtual/tree/VirtualResourceTreeCell.java @@ -0,0 +1,58 @@ +package com.ss.editor.ui.component.virtual.tree; + +import static com.ss.editor.manager.FileIconManager.DEFAULT_FILE_ICON_SIZE; +import com.ss.editor.manager.FileIconManager; +import com.ss.editor.ui.component.virtual.tree.resource.FolderVirtualResourceElement; +import com.ss.editor.ui.component.virtual.tree.resource.VirtualResourceElement; +import com.ss.rlib.util.StringUtils; +import javafx.scene.control.TreeCell; +import javafx.scene.control.TreeView; +import javafx.scene.image.ImageView; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.nio.file.Path; +import java.nio.file.Paths; + +/** + * The implementation of the cell for {@link TreeView} to show resource. + * + * @author JavaSaBr + */ +public class VirtualResourceTreeCell extends TreeCell> { + + /** + * The icon manager. + */ + @NotNull + private static final FileIconManager ICON_MANAGER = FileIconManager.getInstance(); + + /** + * The icon. + */ + @NotNull + private final ImageView icon; + + protected VirtualResourceTreeCell() { + this.icon = new ImageView(); + } + + @Override + protected void updateItem(@Nullable final VirtualResourceElement item, boolean empty) { + super.updateItem(item, empty); + + if (item == null) { + setText(StringUtils.EMPTY); + setGraphic(null); + return; + } + + final Path file = Paths.get(item.getPath()); + final boolean folder = item instanceof FolderVirtualResourceElement; + + icon.setImage(ICON_MANAGER.getIcon(file, folder, false, DEFAULT_FILE_ICON_SIZE)); + + setText(item.getName()); + setGraphic(icon); + } +} diff --git a/src/main/java/com/ss/editor/ui/component/virtual/tree/resource/FolderVirtualResourceElement.java b/src/main/java/com/ss/editor/ui/component/virtual/tree/resource/FolderVirtualResourceElement.java new file mode 100644 index 00000000..ce41fce9 --- /dev/null +++ b/src/main/java/com/ss/editor/ui/component/virtual/tree/resource/FolderVirtualResourceElement.java @@ -0,0 +1,45 @@ +package com.ss.editor.ui.component.virtual.tree.resource; + +import com.ss.editor.annotation.FromAnyThread; +import com.ss.editor.ui.component.virtual.tree.VirtualResourceTree; +import com.ss.rlib.util.array.Array; +import com.ss.rlib.util.array.ArrayFactory; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * The implementation of fake folder resource element. + * + * @author JavaSaBr + */ +public class FolderVirtualResourceElement extends VirtualResourceElement { + + /** + * The list of children. + */ + @NotNull + private final Array> children; + + public FolderVirtualResourceElement(@NotNull final VirtualResourceTree resourceTree, @NotNull final String path) { + super(resourceTree, path); + this.children = ArrayFactory.newArray(VirtualResourceElement.class); + } + + @Override + @FromAnyThread + public void addChild(@NotNull final VirtualResourceElement child) { + this.children.add(child); + } + + @Override + @FromAnyThread + public @Nullable Array> getChildren() { + return children; + } + + @Override + @FromAnyThread + public boolean hasChildren() { + return !children.isEmpty(); + } +} diff --git a/src/main/java/com/ss/editor/ui/component/virtual/tree/resource/ObjectVirtualResourceElement.java b/src/main/java/com/ss/editor/ui/component/virtual/tree/resource/ObjectVirtualResourceElement.java new file mode 100644 index 00000000..f12edb63 --- /dev/null +++ b/src/main/java/com/ss/editor/ui/component/virtual/tree/resource/ObjectVirtualResourceElement.java @@ -0,0 +1,17 @@ +package com.ss.editor.ui.component.virtual.tree.resource; + +import com.ss.editor.ui.component.virtual.tree.VirtualResourceTree; +import org.jetbrains.annotations.NotNull; + +/** + * The default implementation of the virtual resource element. + * + * @param the type of presented object. + * @author JavaSaBr + */ +public class ObjectVirtualResourceElement extends VirtualResourceElement { + + public ObjectVirtualResourceElement(@NotNull final VirtualResourceTree resourceTree, @NotNull final T object) { + super(resourceTree, object); + } +} diff --git a/src/main/java/com/ss/editor/ui/component/virtual/tree/resource/RootVirtualResourceElement.java b/src/main/java/com/ss/editor/ui/component/virtual/tree/resource/RootVirtualResourceElement.java new file mode 100644 index 00000000..eed6d7a1 --- /dev/null +++ b/src/main/java/com/ss/editor/ui/component/virtual/tree/resource/RootVirtualResourceElement.java @@ -0,0 +1,16 @@ +package com.ss.editor.ui.component.virtual.tree.resource; + +import com.ss.editor.ui.component.virtual.tree.VirtualResourceTree; +import org.jetbrains.annotations.NotNull; + +/** + * The root implementation of the virtual resource element. + * + * @author JavaSaBr + */ +public class RootVirtualResourceElement extends FolderVirtualResourceElement { + + public RootVirtualResourceElement(@NotNull final VirtualResourceTree resourceTree) { + super(resourceTree, "/"); + } +} diff --git a/src/main/java/com/ss/editor/ui/component/virtual/tree/resource/VirtualResourceElement.java b/src/main/java/com/ss/editor/ui/component/virtual/tree/resource/VirtualResourceElement.java new file mode 100644 index 00000000..9ed853ae --- /dev/null +++ b/src/main/java/com/ss/editor/ui/component/virtual/tree/resource/VirtualResourceElement.java @@ -0,0 +1,116 @@ +package com.ss.editor.ui.component.virtual.tree.resource; + +import static com.ss.rlib.util.ClassUtils.unsafeCast; +import com.ss.editor.annotation.FromAnyThread; +import com.ss.editor.ui.component.virtual.tree.VirtualResourceTree; +import com.ss.rlib.logging.Logger; +import com.ss.rlib.logging.LoggerManager; +import com.ss.rlib.util.array.Array; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * The base implementation of a virtual resource. + * + * @author JavaSaBr + */ +public abstract class VirtualResourceElement implements Comparable> { + + /** + * The logger. + */ + @NotNull + protected static final Logger LOGGER = LoggerManager.getLogger(VirtualResourceElement.class); + + /** + * The virtual resource tree. + */ + @NotNull + protected final VirtualResourceTree resourceTree; + + /** + * The resource object. + */ + @NotNull + protected final T object; + + public VirtualResourceElement(@NotNull final VirtualResourceTree resourceTree, @NotNull final T object) { + this.resourceTree = resourceTree; + this.object = object; + } + + /** + * Get the type of this resource element. + * + * @return the type of this resource element. + */ + @FromAnyThread + public @NotNull Class getType() { + return unsafeCast(object.getClass()); + } + + /** + * Get the path of this resource. + * + * @return the path of this resource. + */ + @FromAnyThread + public @NotNull String getPath() { + return resourceTree.getPath(object); + } + + /** + * Get the name of this resource. + * + * @return the name of this resource. + */ + @FromAnyThread + public @NotNull String getName() { + return resourceTree.getName(object); + } + + /** + * Get the resource object. + * + * @return the resource object. + */ + @FromAnyThread + public @NotNull T getObject() { + return object; + } + + /** + * Add a child to this element. + * + * @param child the new child. + */ + @FromAnyThread + public void addChild(@NotNull final VirtualResourceElement child) { + + } + + /** + * Get the list of children of this element. + * + * @return list of children resource elements. + */ + @FromAnyThread + public @Nullable Array> getChildren() { + return null; + } + + /** + * Check this element to has children. + * + * @return true if this element has children. + */ + @FromAnyThread + public boolean hasChildren() { + return false; + } + + @Override + public int compareTo(@NotNull final VirtualResourceElement o) { + return 0; + } +} diff --git a/src/main/java/com/ss/editor/ui/component/virtual/tree/resource/VirtualResourceElementFactory.java b/src/main/java/com/ss/editor/ui/component/virtual/tree/resource/VirtualResourceElementFactory.java new file mode 100644 index 00000000..2fc7f5c9 --- /dev/null +++ b/src/main/java/com/ss/editor/ui/component/virtual/tree/resource/VirtualResourceElementFactory.java @@ -0,0 +1,86 @@ +package com.ss.editor.ui.component.virtual.tree.resource; + +import com.ss.editor.annotation.FromAnyThread; +import com.ss.editor.ui.component.virtual.tree.VirtualResourceTree; +import com.ss.rlib.util.FileUtils; +import com.ss.rlib.util.StringUtils; +import com.ss.rlib.util.array.Array; +import com.ss.rlib.util.array.ArrayFactory; +import com.ss.rlib.util.dictionary.DictionaryFactory; +import com.ss.rlib.util.dictionary.ObjectDictionary; +import org.jetbrains.annotations.NotNull; + +/** + * The factory to create a virtual resource elements. + * + * @author JavaSaBr + */ +public class VirtualResourceElementFactory { + + /** + * Build the root virtual resource elements for the resources. + * + * @param resources the resources. + * @return the root element. + */ + @FromAnyThread + public static @NotNull RootVirtualResourceElement build(@NotNull final Array resources, + @NotNull final VirtualResourceTree resourceTree) { + + final ObjectDictionary> parentToChildren = DictionaryFactory.newObjectDictionary(); + final ObjectDictionary pathToResource = DictionaryFactory.newObjectDictionary(); + resources.forEach(element -> pathToResource.put(resourceTree.getPath(element), element)); + + pathToResource.forEach((key, resource) -> { + + String path = key; + String parent = FileUtils.getParent(path, '/'); + + while (!StringUtils.equals(path, parent)) { + + final Array children = parentToChildren.get(parent, () -> ArrayFactory.newArray(String.class)); + if (!children.contains(path)) { + children.add(path); + } + + path = parent; + parent = FileUtils.getParent(path, '/'); + } + }); + + final ObjectDictionary> pathToResult = DictionaryFactory.newObjectDictionary(); + + parentToChildren.forEach((path, children) -> { + if (!pathToResource.containsKey(path)) { + pathToResult.put(path, new FolderVirtualResourceElement(resourceTree, path)); + } + }); + + parentToChildren.forEach((path, children) -> { + + final VirtualResourceElement parent = pathToResult.get(path); + if (parent == null) return; + + for (final String child : children) { + final T resource = pathToResource.get(child); + final VirtualResourceElement element = pathToResult.get(child); + if (resource != null) { + parent.addChild(new ObjectVirtualResourceElement<>(resourceTree, resource)); + } else if (element != null) { + parent.addChild(element); + } + } + }); + + final RootVirtualResourceElement root = new RootVirtualResourceElement(resourceTree); + + pathToResult.forEach((path, element) -> { + final String parent = FileUtils.getParent(path, '/'); + if(StringUtils.equals(parent, path)) { + root.addChild(element); + } + }); + + return root; + } +} diff --git a/src/main/java/com/ss/editor/ui/control/app/state/dialog/CreateSceneAppStateDialog.java b/src/main/java/com/ss/editor/ui/control/app/state/dialog/CreateSceneAppStateDialog.java index 595770c1..f7c36685 100644 --- a/src/main/java/com/ss/editor/ui/control/app/state/dialog/CreateSceneAppStateDialog.java +++ b/src/main/java/com/ss/editor/ui/control/app/state/dialog/CreateSceneAppStateDialog.java @@ -4,6 +4,8 @@ import static com.ss.rlib.util.ObjectUtils.notNull; import static com.ss.rlib.util.dictionary.DictionaryFactory.newObjectDictionary; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.extension.scene.SceneNode; import com.ss.editor.extension.scene.app.state.EditableSceneAppState; import com.ss.editor.extension.scene.app.state.SceneAppState; @@ -41,7 +43,10 @@ public class CreateSceneAppStateDialog extends AbstractSimpleEditorDialog { @NotNull private static final Point DIALOG_SIZE = new Point(415, 0); + @NotNull private static final ObjectDictionary BUILT_IN = newObjectDictionary(); + + @NotNull private static final Array BUILT_IN_NAMES = ArrayFactory.newArray(String.class); static { @@ -80,22 +85,18 @@ private static void register(@NotNull final EditableSceneAppState appState) { @NotNull private final SceneChangeConsumer changeConsumer; - /** - * Instantiates a new Create scene app state dialog. - * - * @param changeConsumer the change consumer - */ public CreateSceneAppStateDialog(@NotNull final SceneChangeConsumer changeConsumer) { this.changeConsumer = changeConsumer; } - @NotNull @Override - protected String getTitleText() { + @FromAnyThread + protected @NotNull String getTitleText() { return Messages.CREATE_SCENE_APP_STATE_DIALOG_TITLE; } @Override + @FXThread protected void createContent(@NotNull final GridPane root) { super.createContent(root); @@ -136,6 +137,7 @@ protected void createContent(@NotNull final GridPane root) { } @Override + @FromAnyThread protected boolean isGridStructure() { return true; } @@ -143,28 +145,29 @@ protected boolean isGridStructure() { /** * @return the check box to chose an option of creating state. */ - @NotNull - private CheckBox getCustomCheckBox() { + @FXThread + private @NotNull CheckBox getCustomCheckBox() { return notNull(customCheckBox); } /** * @return the full class name of creating state. */ - @NotNull - private TextField getStateNameField() { + @FXThread + private @NotNull TextField getStateNameField() { return notNull(stateNameField); } /** * @return the list of built in states. */ - @NotNull - private ComboBox getBuiltInBox() { + @FXThread + private @NotNull ComboBox getBuiltInBox() { return notNull(builtInBox); } @Override + @FXThread protected void processOk() { final CheckBox customCheckBox = getCustomCheckBox(); @@ -202,6 +205,7 @@ protected void processOk() { super.processOk(); } + @FXThread private void check(@NotNull final List appStates, @NotNull final List filters, @NotNull final SceneAppState newExample) { @@ -222,9 +226,9 @@ private void check(@NotNull final List appStates, @NotNull final } } - @NotNull @Override - protected Point getSize() { + @FromAnyThread + protected @NotNull Point getSize() { return DIALOG_SIZE; } } diff --git a/src/main/java/com/ss/editor/ui/control/filter/dialog/CreateSceneFilterDialog.java b/src/main/java/com/ss/editor/ui/control/filter/dialog/CreateSceneFilterDialog.java index 458d8b5c..a99533a1 100644 --- a/src/main/java/com/ss/editor/ui/control/filter/dialog/CreateSceneFilterDialog.java +++ b/src/main/java/com/ss/editor/ui/control/filter/dialog/CreateSceneFilterDialog.java @@ -4,6 +4,8 @@ import static com.ss.rlib.util.ObjectUtils.notNull; import static com.ss.rlib.util.dictionary.DictionaryFactory.newObjectDictionary; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.extension.scene.SceneNode; import com.ss.editor.extension.scene.app.state.SceneAppState; import com.ss.editor.extension.scene.filter.EditableSceneFilter; @@ -93,22 +95,18 @@ private static void register(@NotNull final SceneFilter sceneFilter) { @NotNull private final SceneChangeConsumer changeConsumer; - /** - * Instantiates a new Create scene filter dialog. - * - * @param changeConsumer the change consumer - */ public CreateSceneFilterDialog(@NotNull final SceneChangeConsumer changeConsumer) { this.changeConsumer = changeConsumer; } - @NotNull @Override - protected String getTitleText() { + @FromAnyThread + protected @NotNull String getTitleText() { return Messages.CREATE_SCENE_FILTER_DIALOG_TITLE; } @Override + @FXThread protected void createContent(@NotNull final GridPane root) { super.createContent(root); @@ -146,6 +144,7 @@ protected void createContent(@NotNull final GridPane root) { } @Override + @FromAnyThread protected boolean isGridStructure() { return true; } @@ -153,28 +152,29 @@ protected boolean isGridStructure() { /** * @return the check box to chose an option of creating filter. */ - @NotNull - private CheckBox getCustomCheckBox() { + @FXThread + private @NotNull CheckBox getCustomCheckBox() { return notNull(customCheckBox); } /** * @return the full class name of creating filter. */ - @NotNull - private TextField getFilterNameField() { + @FXThread + private @NotNull TextField getFilterNameField() { return notNull(filterNameField); } /** * @return the list of built in filters. */ - @NotNull - private ComboBox getBuiltInBox() { + @FXThread + private @NotNull ComboBox getBuiltInBox() { return notNull(builtInBox); } @Override + @FXThread protected void processOk() { final SceneNode currentModel = changeConsumer.getCurrentModel(); @@ -233,9 +233,9 @@ private void check(@NotNull final List appStates, @NotNull final } } - @NotNull @Override - protected Point getSize() { + @FromAnyThread + protected @NotNull Point getSize() { return DIALOG_SIZE; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/property/control/particle/MaterialEmitterPropertyControl.java b/src/main/java/com/ss/editor/ui/control/model/property/control/particle/MaterialEmitterPropertyControl.java index 693b6577..db236175 100644 --- a/src/main/java/com/ss/editor/ui/control/model/property/control/particle/MaterialEmitterPropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/model/property/control/particle/MaterialEmitterPropertyControl.java @@ -6,7 +6,7 @@ import com.ss.editor.annotation.FXThread; import com.ss.editor.model.undo.editor.ModelChangeConsumer; import com.ss.editor.ui.control.property.impl.MaterialPropertyControl; -import com.ss.editor.ui.dialog.asset.ParticlesAssetEditorDialog; +import com.ss.editor.ui.dialog.asset.file.ParticlesAssetEditorDialog; import com.ss.editor.ui.event.impl.RequestedOpenFileEvent; import com.ss.rlib.util.StringUtils; import javafx.scene.control.Label; diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/dialog/CreateCustomControlDialog.java b/src/main/java/com/ss/editor/ui/control/model/tree/dialog/CreateCustomControlDialog.java index 78f736ad..0ea94bac 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/dialog/CreateCustomControlDialog.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/dialog/CreateCustomControlDialog.java @@ -5,6 +5,8 @@ import com.jme3.scene.Spatial; import com.jme3.scene.control.Control; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.extension.scene.control.EditableControl; import com.ss.editor.extension.scene.control.impl.EditableBillboardControl; import com.ss.editor.manager.ClasspathManager; @@ -99,49 +101,44 @@ private static void register(@NotNull final EditableControl editableControl) { @NotNull private final Spatial spatial; - /** - * Instantiates a new Create custom control dialog. - * - * @param changeConsumer the change consumer - * @param spatial the spatial - */ public CreateCustomControlDialog(@NotNull final ModelChangeConsumer changeConsumer, @NotNull final Spatial spatial) { this.changeConsumer = changeConsumer; this.spatial = spatial; } - @NotNull @Override - protected String getTitleText() { + @FromAnyThread + protected @NotNull String getTitleText() { return Messages.CREATE_CUSTOM_CONTROL_DIALOG_TITLE; } /** * @return the check box to chose an option of creating control. */ - @NotNull - private CheckBox getCustomCheckBox() { + @FXThread + private @NotNull CheckBox getCustomCheckBox() { return notNull(customCheckBox); } /** * @return the list of built in controls. */ - @NotNull - private ComboBox getBuiltInBox() { + @FXThread + private @NotNull ComboBox getBuiltInBox() { return notNull(builtInBox); } /** * @return the list of available custom controls.. */ - @NotNull - private ComboBox> getCustomComboBox() { + @FXThread + private @NotNull ComboBox> getCustomComboBox() { return notNull(customComboBox); } @Override + @FXThread protected void createContent(@NotNull final GridPane root) { super.createContent(root); @@ -189,11 +186,13 @@ protected void createContent(@NotNull final GridPane root) { } @Override + @FromAnyThread protected boolean isGridStructure() { return true; } @Override + @FXThread protected void processOk() { final CheckBox customCheckBox = getCustomCheckBox(); @@ -221,9 +220,9 @@ protected void processOk() { super.processOk(); } - @NotNull @Override - protected Point getSize() { + @FromAnyThread + protected @NotNull Point getSize() { return DIALOG_SIZE; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/dialog/GenerateTangentsDialog.java b/src/main/java/com/ss/editor/ui/control/model/tree/dialog/GenerateTangentsDialog.java index 0ea64e2f..9eec5313 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/dialog/GenerateTangentsDialog.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/dialog/GenerateTangentsDialog.java @@ -5,6 +5,8 @@ import com.jme3.scene.Geometry; import com.jme3.scene.Mesh; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.model.tool.TangentGenerator; import com.ss.editor.model.undo.editor.ChangeConsumer; import com.ss.editor.ui.control.model.tree.action.operation.ChangeMeshOperation; @@ -80,12 +82,6 @@ public enum AlgorithmType { @Nullable private CheckBox splitMirroredCheckBox; - /** - * Instantiates a new Generate tangents dialog. - * - * @param nodeTree the node tree - * @param node the node - */ public GenerateTangentsDialog(@NotNull final NodeTree nodeTree, @NotNull final TreeNode node) { this.nodeTree = nodeTree; this.node = node; @@ -96,8 +92,8 @@ public GenerateTangentsDialog(@NotNull final NodeTree nodeTree, @NotNull fina * * @return the node tree component. */ - @NotNull - protected NodeTree getNodeTree() { + @FXThread + protected @NotNull NodeTree getNodeTree() { return nodeTree; } @@ -106,18 +102,19 @@ protected NodeTree getNodeTree() { * * @return the generated node. */ - @NotNull - protected TreeNode getNode() { + @FXThread + protected @NotNull TreeNode getNode() { return node; } - @NotNull @Override - protected String getTitleText() { + @FromAnyThread + protected @NotNull String getTitleText() { return Messages.GENERATE_TANGENTS_DIALOG_TITLE; } @Override + @FXThread protected void createContent(@NotNull final GridPane root) { super.createContent(root); @@ -147,6 +144,7 @@ protected void createContent(@NotNull final GridPane root) { } @Override + @FromAnyThread protected boolean isGridStructure() { return true; } @@ -154,20 +152,21 @@ protected boolean isGridStructure() { /** * @return the check box about spliting mirrored. */ - @NotNull - private CheckBox getSplitMirroredCheckBox() { + @FXThread + private @NotNull CheckBox getSplitMirroredCheckBox() { return notNull(splitMirroredCheckBox); } /** * @return the list of types. */ - @NotNull - private ComboBox getAlgorithmTypeComboBox() { + @FXThread + private @NotNull ComboBox getAlgorithmTypeComboBox() { return notNull(algorithmTypeComboBox); } @Override + @FXThread protected void processOk() { final NodeTree nodeTree = getNodeTree(); @@ -193,15 +192,15 @@ protected void processOk() { super.processOk(); } - @NotNull @Override - protected String getButtonOkText() { + @FromAnyThread + protected @NotNull String getButtonOkText() { return Messages.SIMPLE_DIALOG_BUTTON_GENERATE; } - @NotNull @Override - protected Point getSize() { + @FromAnyThread + protected @NotNull Point getSize() { return DIALOG_SIZE; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/dialog/LightSelectorDialog.java b/src/main/java/com/ss/editor/ui/control/model/tree/dialog/LightSelectorDialog.java index 7b545c58..6246b31a 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/dialog/LightSelectorDialog.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/dialog/LightSelectorDialog.java @@ -3,6 +3,7 @@ import com.jme3.light.Light; import com.jme3.scene.Spatial; import com.ss.editor.Messages; +import com.ss.editor.annotation.FromAnyThread; import org.jetbrains.annotations.NotNull; import java.util.function.Consumer; @@ -15,21 +16,14 @@ */ public class LightSelectorDialog extends NodeSelectorDialog { - /** - * Instantiates a new Light selector dialog. - * - * @param model the model - * @param type the type - * @param handler the handler - */ public LightSelectorDialog(@NotNull final Spatial model, @NotNull final Class type, @NotNull final Consumer handler) { super(model, type, handler); } - @NotNull @Override - protected String getTitleText() { + @FromAnyThread + protected @NotNull String getTitleText() { return Messages.LIGHT_SELECTOR_DIALOG_TITLE; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/dialog/NodeSelectorDialog.java b/src/main/java/com/ss/editor/ui/control/model/tree/dialog/NodeSelectorDialog.java index 4328301c..22a2de74 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/dialog/NodeSelectorDialog.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/dialog/NodeSelectorDialog.java @@ -3,6 +3,8 @@ import static com.ss.rlib.util.ObjectUtils.notNull; import com.jme3.scene.Spatial; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.control.model.tree.ModelNodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; import com.ss.editor.ui.css.CSSClasses; @@ -57,13 +59,6 @@ public class NodeSelectorDialog extends AbstractSimpleEditorDialog { @Nullable private T selected; - /** - * Instantiates a new Node selector dialog. - * - * @param model the model - * @param type the type - * @param handler the handler - */ public NodeSelectorDialog(@NotNull final Spatial model, @NotNull final Class type, @NotNull final Consumer handler) { this.model = model; @@ -82,18 +77,19 @@ public NodeSelectorDialog(@NotNull final Spatial model, @NotNull final Class * * @return the model tree component. */ - @NotNull - protected ModelNodeTree getNodeTree() { + @FXThread + protected @NotNull ModelNodeTree getNodeTree() { return notNull(nodeTree); } - @NotNull @Override - protected String getTitleText() { + @FromAnyThread + protected @NotNull String getTitleText() { return Messages.NODE_SELECTOR_DIALOG_TITLE; } @Override + @FXThread protected void createContent(@NotNull final GridPane root) { super.createContent(root); @@ -107,6 +103,7 @@ protected void createContent(@NotNull final GridPane root) { } @Override + @FromAnyThread protected boolean isGridStructure() { return true; } @@ -116,8 +113,8 @@ protected boolean isGridStructure() { * * @return the loaded model. */ - @NotNull - protected Spatial getModel() { + @FXThread + protected @NotNull Spatial getModel() { return model; } @@ -126,14 +123,15 @@ protected Spatial getModel() { * * @return the type of selectable objects. */ - @NotNull - protected Class getType() { + @FXThread + protected @NotNull Class getType() { return type; } /** * Handle a selected object. */ + @FXThread private void processSelect(@Nullable final Object object) { final Object result = object instanceof TreeNode ? ((TreeNode) object).getElement() : object; final Class type = getType(); @@ -143,20 +141,21 @@ private void processSelect(@Nullable final Object object) { } @Override + @FXThread protected void processOk() { handler.accept(selected); super.processOk(); } - @NotNull @Override - protected String getButtonOkText() { + @FromAnyThread + protected @NotNull String getButtonOkText() { return Messages.SIMPLE_DIALOG_BUTTON_SELECT; } - @NotNull @Override - protected Point getSize() { + @FromAnyThread + protected @NotNull Point getSize() { return DIALOG_SIZE; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/dialog/SpatialSelectorDialog.java b/src/main/java/com/ss/editor/ui/control/model/tree/dialog/SpatialSelectorDialog.java index 46c4750e..e37d0ea4 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/dialog/SpatialSelectorDialog.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/dialog/SpatialSelectorDialog.java @@ -2,6 +2,7 @@ import com.jme3.scene.Spatial; import com.ss.editor.Messages; +import com.ss.editor.annotation.FromAnyThread; import org.jetbrains.annotations.NotNull; import java.util.function.Consumer; @@ -14,21 +15,14 @@ */ public class SpatialSelectorDialog extends NodeSelectorDialog { - /** - * Instantiates a new spatial selector dialog. - * - * @param model the model - * @param type the type - * @param handler the handler - */ public SpatialSelectorDialog(@NotNull final Spatial model, @NotNull final Class type, @NotNull final Consumer handler) { super(model, type, handler); } - @NotNull @Override - protected String getTitleText() { + @FromAnyThread + protected @NotNull String getTitleText() { return Messages.NODE_SELECTOR_DIALOG_TITLE; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/dialog/animation/ExtractSubAnimationDialog.java b/src/main/java/com/ss/editor/ui/control/model/tree/dialog/animation/ExtractSubAnimationDialog.java index 9ecbc9f1..90f7f5f4 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/dialog/animation/ExtractSubAnimationDialog.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/dialog/animation/ExtractSubAnimationDialog.java @@ -5,6 +5,9 @@ import com.jme3.animation.AnimControl; import com.jme3.animation.Animation; import com.ss.editor.Messages; +import com.ss.editor.annotation.BackgroundThread; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.manager.ExecutorManager; import com.ss.editor.model.undo.editor.ChangeConsumer; import com.ss.editor.ui.control.model.node.control.anim.AnimationTreeNode; @@ -67,14 +70,7 @@ public class ExtractSubAnimationDialog extends AbstractSimpleEditorDialog { @Nullable private IntegerTextField endFrameField; - /** - * Instantiates a new Extract sub animation dialog. - * - * @param nodeTree the node tree - * @param node the node - */ - public ExtractSubAnimationDialog(@NotNull final NodeTree nodeTree, - @NotNull final AnimationTreeNode node) { + public ExtractSubAnimationDialog(@NotNull final NodeTree nodeTree, @NotNull final AnimationTreeNode node) { this.nodeTree = nodeTree; this.node = node; @@ -99,8 +95,8 @@ public ExtractSubAnimationDialog(@NotNull final NodeTree nodeTree, * * @return the node tree component. */ - @NotNull - protected NodeTree getNodeTree() { + @FXThread + protected @NotNull NodeTree getNodeTree() { return nodeTree; } @@ -109,18 +105,19 @@ protected NodeTree getNodeTree() { * * @return the animation node. */ - @NotNull - protected AnimationTreeNode getNode() { + @FXThread + protected @NotNull AnimationTreeNode getNode() { return node; } - @NotNull @Override - protected String getTitleText() { + @FromAnyThread + protected @NotNull String getTitleText() { return Messages.MANUAL_EXTRACT_ANIMATION_DIALOG_TITLE; } @Override + @FXThread protected void createContent(@NotNull final GridPane root) { super.createContent(root); @@ -154,6 +151,7 @@ protected void createContent(@NotNull final GridPane root) { } @Override + @FromAnyThread protected boolean isGridStructure() { return true; } @@ -161,28 +159,29 @@ protected boolean isGridStructure() { /** * @return the field with a value of new animation name. */ - @NotNull - private TextField getNameField() { + @FXThread + private @NotNull TextField getNameField() { return notNull(nameField); } /** * @return the field with a value of start frame. */ - @NotNull - private IntegerTextField getStartFrameField() { + @FXThread + private @NotNull IntegerTextField getStartFrameField() { return notNull(startFrameField); } /** * @return the field with a value of end frame. */ - @NotNull - private IntegerTextField getEndFrameField() { + @FXThread + private @NotNull IntegerTextField getEndFrameField() { return notNull(endFrameField); } @Override + @FXThread protected void processOk() { EditorUtil.incrementLoading(); EXECUTOR_MANAGER.addBackgroundTask(this::processExtract); @@ -192,6 +191,7 @@ protected void processOk() { /** * Process of extraction a sub animation. */ + @BackgroundThread private void processExtract() { final AnimationTreeNode node = getNode(); @@ -218,15 +218,15 @@ private void processExtract() { EXECUTOR_MANAGER.addFXTask(EditorUtil::decrementLoading); } - @NotNull @Override - protected String getButtonOkText() { + @FromAnyThread + protected @NotNull String getButtonOkText() { return Messages.MANUAL_EXTRACT_ANIMATION_DIALOG_BUTTON_OK; } - @NotNull @Override - protected Point getSize() { + @FromAnyThread + protected @NotNull Point getSize() { return DIALOG_SIZE; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/dialog/geometry/GeometrySelectorDialog.java b/src/main/java/com/ss/editor/ui/control/model/tree/dialog/geometry/GeometrySelectorDialog.java index 8ff2bafe..d10c1586 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/dialog/geometry/GeometrySelectorDialog.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/dialog/geometry/GeometrySelectorDialog.java @@ -3,6 +3,7 @@ import com.jme3.scene.Geometry; import com.jme3.scene.Spatial; import com.ss.editor.Messages; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.control.model.tree.dialog.NodeSelectorDialog; import org.jetbrains.annotations.NotNull; @@ -16,19 +17,13 @@ */ public class GeometrySelectorDialog extends NodeSelectorDialog { - /** - * Instantiates a new Geometry selector dialog. - * - * @param model the model - * @param handler the handler - */ public GeometrySelectorDialog(@NotNull final Spatial model, @NotNull final Consumer handler) { super(model, Geometry.class, handler); } - @NotNull @Override - protected String getTitleText() { + @FromAnyThread + protected @NotNull String getTitleText() { return Messages.GEOMETRY_SELECTOR_DIALOG_TITLE; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/dialog/geometry/lod/GenerateLodLevelsDialog.java b/src/main/java/com/ss/editor/ui/control/model/tree/dialog/geometry/lod/GenerateLodLevelsDialog.java index 7882b1ac..c011d3a4 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/dialog/geometry/lod/GenerateLodLevelsDialog.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/dialog/geometry/lod/GenerateLodLevelsDialog.java @@ -7,6 +7,8 @@ import com.jme3.scene.Mesh; import com.jme3.scene.VertexBuffer; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.manager.ExecutorManager; import com.ss.editor.model.undo.editor.ChangeConsumer; import com.ss.editor.ui.FXConstants; @@ -110,6 +112,7 @@ public GenerateLodLevelsDialog(@NotNull final NodeTree nodeTree, final @NotNu /** * @return the node tree component. */ + @FXThread private @NotNull NodeTree getNodeTree() { return nodeTree; } @@ -119,6 +122,7 @@ public GenerateLodLevelsDialog(@NotNull final NodeTree nodeTree, final @NotNu * * @return the mesh. */ + @FXThread public @NotNull Mesh getMesh() { return mesh; } @@ -126,6 +130,7 @@ public GenerateLodLevelsDialog(@NotNull final NodeTree nodeTree, final @NotNu /** * @return the geometry. */ + @FXThread private @NotNull Geometry getGeometry() { return geometry; } @@ -135,6 +140,7 @@ public GenerateLodLevelsDialog(@NotNull final NodeTree nodeTree, final @NotNu * * @return the reduction method. */ + @FXThread public @NotNull ReductionMethod getMethod() { final ComboBox comboBox = getReductionMethodComboBox(); final SingleSelectionModel selectionModel = comboBox.getSelectionModel(); @@ -144,16 +150,19 @@ public GenerateLodLevelsDialog(@NotNull final NodeTree nodeTree, final @NotNu /** * @return the list view with levels of LoD. */ + @FXThread private @NotNull ListView getLevelsList() { return notNull(levelsList); } @Override + @FromAnyThread protected @NotNull String getTitleText() { return Messages.GENERATE_LOD_DIALOG_TITLE; } @Override + @FXThread protected void createContent(@NotNull final VBox root) { super.createContent(root); @@ -213,6 +222,7 @@ protected void createContent(@NotNull final VBox root) { /** * Update disabling of OK button. */ + @FXThread private void updateButtonOk() { final ListView levelsList = getLevelsList(); @@ -225,6 +235,7 @@ private void updateButtonOk() { /** * Clear added levels. */ + @FXThread private void clearLevels() { final ListView levelsList = getLevelsList(); final ObservableList items = levelsList.getItems(); @@ -234,6 +245,7 @@ private void clearLevels() { /** * Remove a selected level. */ + @FXThread private void processRemove() { final ListView levelsList = getLevelsList(); @@ -246,6 +258,7 @@ private void processRemove() { /** * Add a new level. */ + @FXThread private void processAdd() { final ListView levelsList = getLevelsList(); @@ -260,17 +273,20 @@ private void processAdd() { } @Override + @FXThread protected void processKey(@NotNull final KeyEvent event) { } /** * @return the list of reduction methods. */ + @FXThread private @NotNull ComboBox getReductionMethodComboBox() { return notNull(reductionMethodComboBox); } @Override + @FXThread protected void processOk() { EditorUtil.incrementLoading(); EXECUTOR_MANAGER.addBackgroundTask(this::processGenerate); @@ -280,6 +296,7 @@ protected void processOk() { /** * Process of generating. */ + @FXThread private void processGenerate() { final Geometry geometry = getGeometry(); @@ -325,11 +342,13 @@ private void processGenerate() { } @Override + @FromAnyThread protected @NotNull String getButtonOkText() { return Messages.SIMPLE_DIALOG_BUTTON_GENERATE; } @Override + @FromAnyThread protected @NotNull Point getSize() { return DIALOG_SIZE; } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/dialog/sky/CreateSkyDialog.java b/src/main/java/com/ss/editor/ui/control/model/tree/dialog/sky/CreateSkyDialog.java index ff25a2af..04741bc2 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/dialog/sky/CreateSkyDialog.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/dialog/sky/CreateSkyDialog.java @@ -19,6 +19,8 @@ import com.ss.editor.JFXApplication; import com.ss.editor.Messages; import com.ss.editor.annotation.BackgroundThread; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.extension.util.SSSkyFactory; import com.ss.editor.manager.ExecutorManager; import com.ss.editor.model.undo.editor.ChangeConsumer; @@ -248,18 +250,19 @@ public CreateSkyDialog(@NotNull final TreeNode parentNode, /** * @return the list of sky types. */ - @NotNull - private ComboBox getSkyTypeComboBox() { + @FXThread + private @NotNull ComboBox getSkyTypeComboBox() { return notNull(skyTypeComboBox); } - @NotNull @Override - protected String getTitleText() { + @FromAnyThread + protected @NotNull String getTitleText() { return Messages.CREATE_SKY_DIALOG_TITLE; } @Override + @FXThread protected void createContent(@NotNull final VBox root) { super.createContent(root); @@ -343,6 +346,7 @@ protected void createContent(@NotNull final VBox root) { /** * Create multiple textures settings. */ + @FXThread private void createMultipleTextureSettings() { multipleTextureSettings = new GridPane(); @@ -409,6 +413,7 @@ private void createMultipleTextureSettings() { /** * @return true id need to use SS factory. */ + @FXThread protected boolean isEditableSky() { return false; } @@ -416,6 +421,7 @@ protected boolean isEditableSky() { /** * Create single texture settings. */ + @FXThread private void createSingleTextureSettings() { singleTextureSettings = new GridPane(); @@ -456,30 +462,31 @@ private void createSingleTextureSettings() { /** * @return the settings root. */ - @NotNull - private VBox getSettingsRoot() { + @FXThread + private @NotNull VBox getSettingsRoot() { return notNull(settingsRoot); } /** * @return the container of single texture settings. */ - @NotNull - private GridPane getSingleTextureSettings() { + @FXThread + private @NotNull GridPane getSingleTextureSettings() { return notNull(singleTextureSettings); } /** * @return the container of multiply texture settings. */ - @NotNull - private GridPane getMultipleTextureSettings() { + @FXThread + private @NotNull GridPane getMultipleTextureSettings() { return notNull(multipleTextureSettings); } /** * Handle changing sky type. */ + @FXThread private void processChange(@NotNull final SkyType newValue) { final VBox settingsRoot = getSettingsRoot(); @@ -506,86 +513,87 @@ private void processChange(@NotNull final SkyType newValue) { /** * @return the single texture control. */ - @NotNull - private ChooseTextureControl getSingleTextureControl() { + @FXThread + private @NotNull ChooseTextureControl getSingleTextureControl() { return notNull(singleTextureControl); } /** * @return the list of env types. */ - @NotNull - private ComboBox getEnvMapTypeComboBox() { + @FXThread + private @NotNull ComboBox getEnvMapTypeComboBox() { return notNull(envMapTypeComboBox); } /** * @return the material folder control. */ - @NotNull - private ChooseFolderControl getMaterialFolderControl() { + @FXThread + private @NotNull ChooseFolderControl getMaterialFolderControl() { return notNull(materialFolderControl); } /** * @return the material name field. */ - @NotNull - private TextField getMaterialNameField() { + @FXThread + private @NotNull TextField getMaterialNameField() { return notNull(materialNameField); } /** * @return the top texture control. */ - @NotNull - private ChooseTextureControl getTopTextureControl() { + @FXThread + private @NotNull ChooseTextureControl getTopTextureControl() { return notNull(topTextureControl); } /** * @return the bottom texture control. */ - @NotNull - private ChooseTextureControl getBottomTextureControl() { + @FXThread + private @NotNull ChooseTextureControl getBottomTextureControl() { return notNull(bottomTextureControl); } /** * @return the north texture control. */ - @NotNull - private ChooseTextureControl getNorthTextureControl() { + @FXThread + private @NotNull ChooseTextureControl getNorthTextureControl() { return notNull(northTextureControl); } /** * @return the south texture control. */ - @NotNull - private ChooseTextureControl getSouthTextureControl() { + @FXThread + private @NotNull ChooseTextureControl getSouthTextureControl() { return notNull(southTextureControl); } /** * @return the east texture control. */ - @NotNull - private ChooseTextureControl getEastTextureControl() { + @FXThread + private @NotNull ChooseTextureControl getEastTextureControl() { return notNull(eastTextureControl); } /** * @return the west texture control. */ - @NotNull - private ChooseTextureControl getWestTextureControl() { + @FXThread + private @NotNull ChooseTextureControl getWestTextureControl() { return notNull(westTextureControl); } /** * Validate the dialog. */ + @FXThread private void validate() { if (!isReady()) return; @@ -655,52 +663,53 @@ private void validate() { /** * @return the check box for flipping. */ - @NotNull - private CheckBox getFlipYCheckBox() { + @FXThread + private @NotNull CheckBox getFlipYCheckBox() { return notNull(flipYCheckBox); } /** * @return the scale control for X. */ - @NotNull - private FloatTextField getNormalScaleXField() { + @FXThread + private @NotNull FloatTextField getNormalScaleXField() { return notNull(normalScaleXField); } /** * @return the scale control for Y. */ - @NotNull - private FloatTextField getNormalScaleYField() { + @FXThread + private @NotNull FloatTextField getNormalScaleYField() { return notNull(normalScaleYField); } /** * @return the scale control for Z. */ - @NotNull - private FloatTextField getNormalScaleZField() { + @FXThread + private @NotNull FloatTextField getNormalScaleZField() { return notNull(normalScaleZField); } /** * @return the node tree. */ - @NotNull - private NodeTree getNodeTree() { + @FXThread + private @NotNull NodeTree getNodeTree() { return nodeTree; } /** * @return the parent node. */ - @NotNull - private TreeNode getParentNode() { + @FXThread + private @NotNull TreeNode getParentNode() { return parentNode; } @Override + @FXThread protected void processOk() { EditorUtil.incrementLoading(); @@ -721,6 +730,7 @@ protected void processOk() { /** * The process of creating a new sky. */ + @FXThread private void createSkyInBackground() { final AssetManager assetManager = EDITOR.getAssetManager(); @@ -751,6 +761,7 @@ private void createSkyInBackground() { /** * Create a new sky using multiply textures. */ + @FXThread private void createMultipleTexture(@NotNull final AssetManager assetManager, @NotNull final ChangeConsumer changeConsumer, @NotNull final Vector3f scale) { @@ -805,6 +816,7 @@ private void createMultipleTexture(@NotNull final AssetManager assetManager, /** * Create a new sky using a single texture. */ + @FXThread private void createSingleTexture(@NotNull final AssetManager assetManager, @NotNull final ChangeConsumer changeConsumer, @NotNull final Vector3f scale) { @@ -844,7 +856,7 @@ private void createSingleTexture(@NotNull final AssetManager assetManager, * @param geometry the sky geometry. */ @BackgroundThread - private Material createMaterialFileIfNeed(@NotNull final Geometry geometry) { + private @NotNull Material createMaterialFileIfNeed(@NotNull final Geometry geometry) { final TextField materialNameField = getMaterialNameField(); final ChooseFolderControl materialFolderControl = getMaterialFolderControl(); @@ -868,9 +880,9 @@ private Material createMaterialFileIfNeed(@NotNull final Geometry geometry) { return assetManager.loadMaterial(assetPath); } - @NotNull @Override - protected Point getSize() { + @FromAnyThread + protected @NotNull Point getSize() { return DIALOG_SIZE; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/tree/dialog/terrain/CreateTerrainDialog.java b/src/main/java/com/ss/editor/ui/control/model/tree/dialog/terrain/CreateTerrainDialog.java index d2e0a9d9..d88cc039 100644 --- a/src/main/java/com/ss/editor/ui/control/model/tree/dialog/terrain/CreateTerrainDialog.java +++ b/src/main/java/com/ss/editor/ui/control/model/tree/dialog/terrain/CreateTerrainDialog.java @@ -22,6 +22,8 @@ import com.ss.editor.JFXApplication; import com.ss.editor.Messages; import com.ss.editor.annotation.BackgroundThread; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.manager.ExecutorManager; import com.ss.editor.model.undo.editor.ChangeConsumer; import com.ss.editor.ui.control.choose.ChooseFolderControl; @@ -263,12 +265,6 @@ public String toString() { @Nullable private FloatTextField hillMaxRadiusField; - /** - * Instantiates a new Create terrain dialog. - * - * @param parentNode the parent node - * @param nodeTree the node tree - */ public CreateTerrainDialog(@NotNull final TreeNode parentNode, @NotNull final NodeTree nodeTree) { this.parentNode = parentNode; this.nodeTree = nodeTree; @@ -289,6 +285,7 @@ public CreateTerrainDialog(@NotNull final TreeNode parentNode, @NotNull final } @Override + @FXThread protected void createContent(@NotNull final VBox root) { super.createContent(root); @@ -448,6 +445,7 @@ protected void createContent(@NotNull final VBox root) { /** * Update a list of available path sizes. */ + @FXThread private void updatePathSizeValues() { final ComboBox pathSizeComboBox = getPatchSizeComboBox(); @@ -475,118 +473,119 @@ private void updatePathSizeValues() { /** * @return the total size combo box. */ - @NotNull - private ComboBox getTotalSizeComboBox() { + @FXThread + private @NotNull ComboBox getTotalSizeComboBox() { return notNull(totalSizeComboBox); } /** * @return the patch size combo box. */ - @NotNull - private ComboBox getPatchSizeComboBox() { + @FXThread + private @NotNull ComboBox getPatchSizeComboBox() { return notNull(patchSizeComboBox); } /** * @return the alpha blend texture size combo box. */ - @NotNull - private ComboBox getAlphaBlendTextureSizeComboBox() { + @FXThread + private @NotNull ComboBox getAlphaBlendTextureSizeComboBox() { return notNull(alphaBlendTextureSizeComboBox); } /** * @return the min radius field. */ - @NotNull - private FloatTextField getHillMinRadiusField() { + @FXThread + private @NotNull FloatTextField getHillMinRadiusField() { return notNull(hillMinRadiusField); } /** * @return the max radius field. */ - @NotNull - private FloatTextField getHillMaxRadiusField() { + @FXThread + private @NotNull FloatTextField getHillMaxRadiusField() { return notNull(hillMaxRadiusField); } /** * @return the settingsRoot. */ - @NotNull - private VBox getSettingsRoot() { + @FXThread + private @NotNull VBox getSettingsRoot() { return notNull(settingsRoot); } /** * @return the base texture control. */ - @NotNull - private ChooseTextureControl getBaseTextureControl() { + @FXThread + private @NotNull ChooseTextureControl getBaseTextureControl() { return notNull(baseTextureControl); } /** * @return the base image control. */ - @NotNull - private ChooseTextureControl getHeightMapImageControl() { + @FXThread + private @NotNull ChooseTextureControl getHeightMapImageControl() { return notNull(heightMapImageControl); } /** * @return the type of height map. */ - @NotNull - private ComboBox getHeightMapTypeComboBox() { + @FXThread + private @NotNull ComboBox getHeightMapTypeComboBox() { return notNull(heightMapTypeComboBox); } /** * @return the height map scale field. */ - @NotNull - private FloatTextField getHeightMapScaleField() { + @FXThread + private @NotNull FloatTextField getHeightMapScaleField() { return notNull(heightMapScaleField); } /** * @return the height map smooth field. */ - @NotNull - private FloatTextField getHeightMapSmoothField() { + @FXThread + private @NotNull FloatTextField getHeightMapSmoothField() { return notNull(heightMapSmoothField); } /** * @return the flattening field. */ - @NotNull - private IntegerTextField getHillFlatteningField() { + @FXThread + private @NotNull IntegerTextField getHillFlatteningField() { return notNull(hillFlatteningField); } /** * @return the iterations field. */ - @NotNull - private IntegerTextField getHillIterationsField() { + @FXThread + private @NotNull IntegerTextField getHillIterationsField() { return notNull(hillIterationsField); } /** * @return the alpha texture folder control. */ - @NotNull - private ChooseFolderControl getAlphaTextureFolderControl() { + @FXThread + private @NotNull ChooseFolderControl getAlphaTextureFolderControl() { return notNull(alphaTextureFolderControl); } /** * Handle changing type of heightmap. */ + @FXThread private void processChangeType(@NotNull final HeightMapType newValue) { final VBox root = getSettingsRoot(); @@ -615,6 +614,7 @@ private void processChangeType(@NotNull final HeightMapType newValue) { /** * Validate. */ + @FXThread private void validate() { final FloatTextField hillMaxRadiusField = getHillMaxRadiusField(); @@ -647,6 +647,7 @@ private void validate() { } @Override + @FXThread protected void processOk() { super.processOk(); EditorUtil.incrementLoading(); @@ -813,31 +814,31 @@ private void createTerrainInBackground() throws Exception { changeConsumer.execute(new AddChildOperation(terrainNode, parent)); } - @NotNull @Override - protected String getTitleText() { + @FromAnyThread + protected @NotNull String getTitleText() { return Messages.CREATE_TERRAIN_DIALOG_TITLE; } /** * @return the node tree. */ - @NotNull - private NodeTree getNodeTree() { + @FXThread + private @NotNull NodeTree getNodeTree() { return nodeTree; } /** * @return the parent node. */ - @NotNull - private TreeNode getParentNode() { + @FXThread + private @NotNull TreeNode getParentNode() { return parentNode; } - @NotNull @Override - protected Point getSize() { + @FromAnyThread + protected @NotNull Point getSize() { return DIALOG_SIZE; } } diff --git a/src/main/java/com/ss/editor/ui/dialog/AbstractSimpleEditorDialog.java b/src/main/java/com/ss/editor/ui/dialog/AbstractSimpleEditorDialog.java index 301a5b51..cebef51e 100644 --- a/src/main/java/com/ss/editor/ui/dialog/AbstractSimpleEditorDialog.java +++ b/src/main/java/com/ss/editor/ui/dialog/AbstractSimpleEditorDialog.java @@ -2,6 +2,7 @@ import com.ss.editor.Messages; import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.manager.ExecutorManager; import com.ss.editor.ui.css.CSSClasses; import com.ss.editor.util.EditorUtil; @@ -97,6 +98,7 @@ protected boolean isReady() { } @Override + @FXThread protected void processKey(@NotNull final KeyEvent event) { super.processKey(event); final Button okButton = getOkButton(); @@ -106,6 +108,7 @@ protected void processKey(@NotNull final KeyEvent event) { } @Override + @FXThread protected void createContent(@NotNull final GridPane root) { super.createContent(root); } @@ -115,6 +118,7 @@ protected void createContent(@NotNull final GridPane root) { * * @return the ok button. */ + @FXThread protected @Nullable Button getOkButton() { return okButton; } @@ -124,19 +128,19 @@ protected void createContent(@NotNull final GridPane root) { * * @return the close button. */ + @FXThread protected @Nullable Button getCloseButton() { return closeButton; } @Override + @FXThread protected void createActions(@NotNull final VBox root) { super.createActions(root); - HBox container = null; + final HBox container = new HBox(); - if (needCloseButton() || needOkButton()) { - container = new HBox(); - } + createBeforeActions(container); if (needOkButton()) { okButton = new Button(getButtonOkText()); @@ -160,12 +164,18 @@ protected void createActions(@NotNull final VBox root) { createAdditionalActions(container); - if (container != null) { + if (!container.getChildren().isEmpty()) { FXUtils.addToPane(container, root); FXUtils.addClassTo(container, CSSClasses.DEF_HBOX); } } + @FXThread + protected void createBeforeActions(@NotNull final HBox container) { + + } + + @FXThread protected void createAdditionalActions(@NotNull final HBox container) { } @@ -173,6 +183,7 @@ protected void createAdditionalActions(@NotNull final HBox container) { /** * @return true if need to add an ok button here. */ + @FromAnyThread protected boolean needOkButton() { return true; } @@ -180,10 +191,12 @@ protected boolean needOkButton() { /** * @return true if need to add a close button here. */ + @FromAnyThread protected boolean needCloseButton() { return true; } + @FXThread private void safeProcessOk() { try { processOk(); @@ -197,6 +210,7 @@ private void safeProcessOk() { * * @return the the button's close text. */ + @FromAnyThread protected @NotNull String getButtonCloseText() { return Messages.SIMPLE_DIALOG_BUTTON_CLOSE; } @@ -206,6 +220,7 @@ private void safeProcessOk() { * * @return the button's ok text. */ + @FromAnyThread protected @NotNull String getButtonOkText() { return Messages.SIMPLE_DIALOG_BUTTON_OK; } diff --git a/src/main/java/com/ss/editor/ui/dialog/ConfirmDialog.java b/src/main/java/com/ss/editor/ui/dialog/ConfirmDialog.java index e66799d7..5e2fd723 100644 --- a/src/main/java/com/ss/editor/ui/dialog/ConfirmDialog.java +++ b/src/main/java/com/ss/editor/ui/dialog/ConfirmDialog.java @@ -2,6 +2,8 @@ import static com.ss.rlib.util.ObjectUtils.notNull; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.css.CSSClasses; import com.ss.rlib.ui.util.FXUtils; import javafx.scene.control.Button; @@ -38,12 +40,6 @@ public class ConfirmDialog extends AbstractSimpleEditorDialog { @Nullable private Label questionLabel; - /** - * Instantiates a new Confirm dialog. - * - * @param handler the handler - * @param question the question - */ public ConfirmDialog(@NotNull final Consumer<@Nullable Boolean> handler, @NotNull final String question) { this.handler = handler; final Label questionLabel = getQuestionLabel(); @@ -53,16 +49,19 @@ public ConfirmDialog(@NotNull final Consumer<@Nullable Boolean> handler, @NotNul /** * @return the label. */ + @FromAnyThread private @NotNull Label getQuestionLabel() { return notNull(questionLabel); } @Override + @FromAnyThread protected @NotNull String getTitleText() { return Messages.QUESTION_DIALOG_TITLE; } @Override + @FXThread protected void createContent(@NotNull final VBox root) { super.createContent(root); @@ -74,16 +73,19 @@ protected void createContent(@NotNull final VBox root) { } @Override + @FromAnyThread protected @NotNull String getButtonOkText() { return Messages.SIMPLE_DIALOG_BUTTON_YES; } @Override + @FromAnyThread protected @NotNull String getButtonCloseText() { return Messages.SIMPLE_DIALOG_BUTTON_NO; } @Override + @FXThread protected void processKey(@NotNull final KeyEvent event) { if (event.getCode() == KeyCode.ENTER) { processClose(); @@ -91,12 +93,14 @@ protected void processKey(@NotNull final KeyEvent event) { } @Override + @FXThread protected void processOk() { super.processOk(); handler.accept(Boolean.TRUE); } @Override + @FXThread protected void processClose() { super.processClose(); handler.accept(Boolean.FALSE); @@ -111,11 +115,13 @@ protected void processCancel() { } @Override + @FromAnyThread protected @NotNull Point getSize() { return DIALOG_SIZE; } @Override + @FXThread protected void createAdditionalActions(@NotNull final HBox container) { super.createAdditionalActions(container); diff --git a/src/main/java/com/ss/editor/ui/dialog/EditorDialog.java b/src/main/java/com/ss/editor/ui/dialog/EditorDialog.java index cac193b9..766a04f0 100644 --- a/src/main/java/com/ss/editor/ui/dialog/EditorDialog.java +++ b/src/main/java/com/ss/editor/ui/dialog/EditorDialog.java @@ -125,6 +125,7 @@ public EditorDialog() { /** * @return true if this dialog should be resizable. */ + @FromAnyThread protected boolean isResizable() { return true; } @@ -324,6 +325,7 @@ public void show(@NotNull final Window owner) { * * @return the dialog id */ + @FromAnyThread protected @NotNull String getDialogId() { return getClass().getSimpleName(); } diff --git a/src/main/java/com/ss/editor/ui/dialog/RenameDialog.java b/src/main/java/com/ss/editor/ui/dialog/RenameDialog.java index d1845313..f8ea855d 100644 --- a/src/main/java/com/ss/editor/ui/dialog/RenameDialog.java +++ b/src/main/java/com/ss/editor/ui/dialog/RenameDialog.java @@ -2,6 +2,8 @@ import static com.ss.rlib.util.ObjectUtils.notNull; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.css.CSSClasses; import com.ss.rlib.ui.util.FXUtils; import javafx.scene.control.Button; @@ -45,6 +47,7 @@ public class RenameDialog extends AbstractSimpleEditorDialog { private TextField nameField; @Override + @FXThread protected void createContent(@NotNull final GridPane root) { super.createContent(root); @@ -63,19 +66,21 @@ protected void createContent(@NotNull final GridPane root) { } @Override + @FromAnyThread protected boolean isGridStructure() { return true; } @Override + @FXThread public void show(@NotNull final Window owner) { super.show(owner); EXECUTOR_MANAGER.addFXTask(() -> getNameField().requestFocus()); } - @NotNull @Override - protected String getTitleText() { + @FromAnyThread + protected @NotNull String getTitleText() { return Messages.RENAME_DIALOG_TITLE; } @@ -84,6 +89,7 @@ protected String getTitleText() { * * @param initName the initial name. */ + @FXThread public void setInitName(@NotNull final String initName) { final TextField nameField = getNameField(); nameField.setText(initName); @@ -92,16 +98,16 @@ public void setInitName(@NotNull final String initName) { /** * @return the text field. */ - @NotNull - private TextField getNameField() { + @FXThread + private @NotNull TextField getNameField() { return notNull(nameField); } /** * @return the function for validation name. */ - @Nullable - private Function getValidator() { + @FXThread + private @Nullable Function getValidator() { return validator; } @@ -110,6 +116,7 @@ private Function getValidator() { * * @param validator the function for validation name. */ + @FXThread public void setValidator(@Nullable final Function validator) { this.validator = validator; } @@ -117,8 +124,8 @@ public void setValidator(@Nullable final Function validator) { /** * @return the function for handling a new name. */ - @Nullable - private Consumer getHandler() { + @FXThread + private @Nullable Consumer getHandler() { return handler; } @@ -127,6 +134,7 @@ private Consumer getHandler() { * * @param handler the function for handling a new name. */ + @FXThread public void setHandler(@Nullable final Consumer handler) { this.handler = handler; } @@ -134,21 +142,22 @@ public void setHandler(@Nullable final Consumer handler) { /** * Validate a new name. */ + @FXThread private void validateName(@NotNull final String name) { final Function validator = getValidator(); final Button okButton = getOkButton(); okButton.setDisable(!(validator == null || validator.apply(name))); } - @NotNull @Override - protected String getButtonCloseText() { + @FromAnyThread + protected @NotNull String getButtonCloseText() { return Messages.SIMPLE_DIALOG_BUTTON_CANCEL; } - @NotNull @Override - protected String getButtonOkText() { + @FromAnyThread + protected @NotNull String getButtonOkText() { return Messages.RENAME_DIALOG_BUTTON_OK; } @@ -156,6 +165,7 @@ protected String getButtonOkText() { * Finish this dialog. */ @Override + @FXThread protected void processOk() { super.processOk(); @@ -166,9 +176,9 @@ protected void processOk() { handler.accept(nameField.getText()); } - @NotNull @Override - protected Point getSize() { + @FromAnyThread + protected @NotNull Point getSize() { return DIALOG_SIZE; } } diff --git a/src/main/java/com/ss/editor/ui/dialog/SettingsDialog.java b/src/main/java/com/ss/editor/ui/dialog/SettingsDialog.java index 54992b0f..45874121 100644 --- a/src/main/java/com/ss/editor/ui/dialog/SettingsDialog.java +++ b/src/main/java/com/ss/editor/ui/dialog/SettingsDialog.java @@ -9,6 +9,8 @@ import com.ss.editor.Editor; import com.ss.editor.JFXApplication; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.config.EditorConfig; import com.ss.editor.manager.ClasspathManager; import com.ss.editor.manager.ExecutorManager; @@ -241,6 +243,7 @@ public SettingsDialog() { } @Override + @FXThread public void show(@NotNull final Window owner) { super.show(owner); setIgnoreListeners(true); @@ -255,6 +258,7 @@ public void show(@NotNull final Window owner) { /** * @param ignoreListeners the flag of ignoring listeners. */ + @FXThread private void setIgnoreListeners(final boolean ignoreListeners) { this.ignoreListeners = ignoreListeners; } @@ -262,11 +266,13 @@ private void setIgnoreListeners(final boolean ignoreListeners) { /** * @return true of the listeners are ignored. */ + @FXThread private boolean isIgnoreListeners() { return ignoreListeners; } @Override + @FXThread protected void createContent(@NotNull final VBox root) { super.createContent(root); @@ -322,6 +328,7 @@ protected void createContent(@NotNull final VBox root) { /** * Create the libraries folder control. */ + @FXThread private void createLibrariesFolderControl(@NotNull final VBox root) { final HBox container = new HBox(); @@ -360,6 +367,7 @@ private void createLibrariesFolderControl(@NotNull final VBox root) { /** * Create the classes folder control. */ + @FXThread private void createClassesFolderControl(@NotNull final VBox root) { final HBox container = new HBox(); @@ -398,6 +406,7 @@ private void createClassesFolderControl(@NotNull final VBox root) { /** * Create the additional envs control. */ + @FXThread private void createAdditionalEnvsControl(@NotNull final VBox root) { final HBox container = new HBox(); @@ -436,6 +445,7 @@ private void createAdditionalEnvsControl(@NotNull final VBox root) { /** * Process of removing the additional classpath. */ + @FXThread private void processRemoveLibrariesFolder() { setLibrariesFolder(null); @@ -446,6 +456,7 @@ private void processRemoveLibrariesFolder() { /** * Process of removing the additional classpath. */ + @FXThread private void processRemoveClassesFolder() { setClassesFolder(null); @@ -456,6 +467,7 @@ private void processRemoveClassesFolder() { /** * Process of removing the additional envs. */ + @FXThread private void processRemoveEF() { setAdditionalEnvsFolder(null); @@ -466,30 +478,31 @@ private void processRemoveEF() { /** * @return the libraries folder field. */ - @NotNull - private TextField getLibrariesFolderField() { + @FXThread + private @NotNull TextField getLibrariesFolderField() { return notNull(librariesFolderField); } /** * @return the classes folder field. */ - @NotNull - private TextField getClassesFolderField() { + @FXThread + private @NotNull TextField getClassesFolderField() { return notNull(classesFolderField); } /** * @return the additional envs field. */ - @NotNull - private TextField getAdditionalEnvsField() { + @FXThread + private @NotNull TextField getAdditionalEnvsField() { return notNull(additionalEnvsField); } /** * Add a new libraries folder. */ + @FXThread private void processAddLibrariesFolder() { final CheckBox checkBox = getNativeFileChooserCheckBox(); @@ -525,6 +538,7 @@ private void processAddLibrariesFolder() { /** * Add a new classes folder. */ + @FXThread private void processAddClassesFolder() { final CheckBox checkBox = getNativeFileChooserCheckBox(); @@ -560,6 +574,7 @@ private void processAddClassesFolder() { /** * Process of adding the additional envs. */ + @FXThread private void processAddEF() { final CheckBox checkBox = getNativeFileChooserCheckBox(); @@ -595,6 +610,7 @@ private void processAddEF() { /** * Create gamma correction control. */ + @FXThread private void createGammaCorrectionControl(@NotNull final VBox root) { final HBox container = new HBox(); @@ -616,6 +632,7 @@ private void createGammaCorrectionControl(@NotNull final VBox root) { /** * Create tonemap filter control. */ + @FXThread private void createToneMapFilterControl(@NotNull final VBox root) { final HBox container = new HBox(); @@ -637,6 +654,7 @@ private void createToneMapFilterControl(@NotNull final VBox root) { /** * Create white point control. */ + @FXThread private void createToneMapFilterWhitePointControl(@NotNull final VBox root) { final CheckBox filterCheckBox = getToneMapFilterCheckBox(); @@ -708,6 +726,7 @@ private void createToneMapFilterWhitePointControl(@NotNull final VBox root) { /** * The process of scrolling. */ + @FXThread private void processScroll(@NotNull final Spinner spinner, @NotNull final ScrollEvent event) { if (!event.isControlDown()) return; @@ -723,6 +742,7 @@ private void processScroll(@NotNull final Spinner spinner, @NotNull fina /** * Create FXAA control. */ + @FXThread private void createFXAAControl(@NotNull final VBox root) { final HBox container = new HBox(); @@ -744,6 +764,7 @@ private void createFXAAControl(@NotNull final VBox root) { /** * Create native file chooser control. */ + @FXThread private void createNativeFileChooserControl(@NotNull final VBox root) { final HBox container = new HBox(); @@ -765,6 +786,7 @@ private void createNativeFileChooserControl(@NotNull final VBox root) { /** * Create stop render control. */ + @FXThread private void createStopRenderControl(@NotNull final VBox root) { final HBox container = new HBox(); @@ -786,6 +808,7 @@ private void createStopRenderControl(@NotNull final VBox root) { /** * Create the checkbox for configuring enabling google analytics. */ + @FXThread private void createGoogleAnalyticsControl(@NotNull final VBox root) { final HBox container = new HBox(); @@ -807,6 +830,7 @@ private void createGoogleAnalyticsControl(@NotNull final VBox root) { /** * Create the checkbox for configuring auto tangent generating. */ + @FXThread private void createAutoTangentGeneratingControl(@NotNull final VBox root) { final HBox container = new HBox(); @@ -828,6 +852,7 @@ private void createAutoTangentGeneratingControl(@NotNull final VBox root) { /** * Create the checkbox for configuring using flip textures by default. */ + @FXThread private void createUseFlippedTextureDefaultControl(@NotNull final VBox root) { final HBox container = new HBox(); @@ -849,6 +874,7 @@ private void createUseFlippedTextureDefaultControl(@NotNull final VBox root) { /** * Create the checkbox for configuring enabling camera lamp by default. */ + @FXThread private void createDefaultCameraLampEnabledControl(@NotNull final VBox root) { final HBox container = new HBox(); @@ -870,6 +896,7 @@ private void createDefaultCameraLampEnabledControl(@NotNull final VBox root) { /** * Create the anisotropy control */ + @FXThread private void createAnisotropyControl(@NotNull final VBox root) { final HBox container = new HBox(); @@ -897,6 +924,7 @@ private void createAnisotropyControl(@NotNull final VBox root) { /** * Create the theme control */ + @FXThread private void createThemeControl(@NotNull final VBox root) { final HBox container = new HBox(); @@ -924,6 +952,7 @@ private void createThemeControl(@NotNull final VBox root) { /** * Create the open GL control */ + @FXThread private void createOpenGLControl(@NotNull final VBox root) { final HBox container = new HBox(); @@ -951,6 +980,7 @@ private void createOpenGLControl(@NotNull final VBox root) { /** * Create the frame rate control. */ + @FXThread private void createFrameRateControl(@NotNull final VBox root) { final HBox container = new HBox(); @@ -974,6 +1004,7 @@ private void createFrameRateControl(@NotNull final VBox root) { /** * Create the camera angle control. */ + @FXThread private void createCameraAngleControl(@NotNull final VBox root) { final HBox container = new HBox(); @@ -997,6 +1028,7 @@ private void createCameraAngleControl(@NotNull final VBox root) { /** * @return the gamma correction checkbox. */ + @FXThread private @NotNull CheckBox getGammaCorrectionCheckBox() { return notNull(gammaCorrectionCheckBox); } @@ -1004,6 +1036,7 @@ private void createCameraAngleControl(@NotNull final VBox root) { /** * @return the tone map filter checkbox. */ + @FXThread private @NotNull CheckBox getToneMapFilterCheckBox() { return notNull(toneMapFilterCheckBox); } @@ -1011,6 +1044,7 @@ private void createCameraAngleControl(@NotNull final VBox root) { /** * @return the white point X. */ + @FXThread private @NotNull Spinner getToneMapFilterWhitePointX() { return notNull(toneMapFilterWhitePointX); } @@ -1018,6 +1052,7 @@ private void createCameraAngleControl(@NotNull final VBox root) { /** * @return the white point Y. */ + @FXThread private @NotNull Spinner getToneMapFilterWhitePointY() { return notNull(toneMapFilterWhitePointY); } @@ -1025,6 +1060,7 @@ private void createCameraAngleControl(@NotNull final VBox root) { /** * @return the white point Z. */ + @FXThread private @NotNull Spinner getToneMapFilterWhitePointZ() { return notNull(toneMapFilterWhitePointZ); } @@ -1032,6 +1068,7 @@ private void createCameraAngleControl(@NotNull final VBox root) { /** * @return the FXAA checkbox. */ + @FXThread private @NotNull CheckBox getFXAAFilterCheckBox() { return notNull(fxaaFilterCheckBox); } @@ -1039,6 +1076,7 @@ private void createCameraAngleControl(@NotNull final VBox root) { /** * @return the checkbox to enable using native file choosers. */ + @FXThread private @NotNull CheckBox getNativeFileChooserCheckBox() { return notNull(nativeFileChooserCheckBox); } @@ -1046,6 +1084,7 @@ private void createCameraAngleControl(@NotNull final VBox root) { /** * @return the Stop Render On Lost Focus checkbox. */ + @FXThread private @NotNull CheckBox getStopRenderOnLostFocusCheckBox() { return notNull(stopRenderOnLostFocusCheckBox); } @@ -1053,6 +1092,7 @@ private void createCameraAngleControl(@NotNull final VBox root) { /** * @return the list with anisotropy levels. */ + @FXThread private @NotNull ComboBox getAnisotropyComboBox() { return notNull(anisotropyComboBox); } @@ -1060,6 +1100,7 @@ private void createCameraAngleControl(@NotNull final VBox root) { /** * @return The frame rate field. */ + @FXThread private @NotNull IntegerTextField getFrameRateField() { return notNull(frameRateField); } @@ -1067,6 +1108,7 @@ private void createCameraAngleControl(@NotNull final VBox root) { /** * @return the camera angle field. */ + @FXThread private @NotNull IntegerTextField getCameraAngleField() { return notNull(cameraAngleField); } @@ -1074,6 +1116,7 @@ private void createCameraAngleControl(@NotNull final VBox root) { /** * @return the checkbox for enabling auto tangent generating. */ + @FXThread private @NotNull CheckBox getAutoTangentGeneratingCheckBox() { return notNull(autoTangentGeneratingCheckBox); } @@ -1081,6 +1124,7 @@ private void createCameraAngleControl(@NotNull final VBox root) { /** * @return the checkbox for enabling camera lamp by default. */ + @FXThread private @NotNull CheckBox getDefaultCameraLampEnabledCheckBox() { return notNull(defaultCameraLampEnabledCheckBox); } @@ -1088,6 +1132,7 @@ private void createCameraAngleControl(@NotNull final VBox root) { /** * @return the checkbox for enabling use flip texture by default. */ + @FXThread private @NotNull CheckBox getDefaultUseFlippedTextureCheckBox() { return notNull(defaultUseFlippedTextureCheckBox); } @@ -1095,6 +1140,7 @@ private void createCameraAngleControl(@NotNull final VBox root) { /** * @return the checkbox for enabling google analytics. */ + @FXThread private @NotNull CheckBox getGoogleAnalyticsCheckBox() { return notNull(googleAnalyticsCheckBox); } @@ -1102,6 +1148,7 @@ private void createCameraAngleControl(@NotNull final VBox root) { /** * @return the message label. */ + @FXThread private @NotNull Label getMessageLabel() { return notNull(messageLabel); } @@ -1109,6 +1156,7 @@ private void createCameraAngleControl(@NotNull final VBox root) { /** * Validate changes. */ + @FXThread private void validate() { if (isIgnoreListeners()) return; @@ -1154,6 +1202,7 @@ private void validate() { /** * Load current settings. */ + @FXThread private void load() { final EditorConfig editorConfig = EditorConfig.getInstance(); @@ -1242,6 +1291,7 @@ private void load() { /** * @return the list with themes. */ + @FXThread private @NotNull ComboBox getThemeComboBox() { return notNull(themeComboBox); } @@ -1249,6 +1299,7 @@ private void load() { /** * @return the list with open GL versions. */ + @FXThread private @NotNull ComboBox getOpenGLVersionComboBox() { return notNull(openGLVersionComboBox); } @@ -1256,6 +1307,7 @@ private void load() { /** * @return the libraries folder. */ + @FXThread private @Nullable Path getLibrariesFolder() { return librariesFolder; } @@ -1263,6 +1315,7 @@ private void load() { /** * @return the classes folder. */ + @FXThread private @Nullable Path getClassesFolder() { return classesFolder; } @@ -1270,6 +1323,7 @@ private void load() { /** * @param librariesFolder the libraries folder. */ + @FXThread private void setLibrariesFolder(@Nullable final Path librariesFolder) { this.librariesFolder = librariesFolder; } @@ -1277,6 +1331,7 @@ private void setLibrariesFolder(@Nullable final Path librariesFolder) { /** * @param classesFolder the classes folder. */ + @FXThread private void setClassesFolder(@Nullable final Path classesFolder) { this.classesFolder = classesFolder; } @@ -1284,6 +1339,7 @@ private void setClassesFolder(@Nullable final Path classesFolder) { /** * @param additionalEnvsFolder the additional envs folder. */ + @FXThread private void setAdditionalEnvsFolder(@Nullable final Path additionalEnvsFolder) { this.additionalEnvsFolder = additionalEnvsFolder; } @@ -1291,11 +1347,13 @@ private void setAdditionalEnvsFolder(@Nullable final Path additionalEnvsFolder) /** * @return the additional envs folder. */ + @FXThread private @Nullable Path getAdditionalEnvsFolder() { return additionalEnvsFolder; } @Override + @FXThread protected void createActions(@NotNull final VBox root) { super.createActions(root); @@ -1318,6 +1376,7 @@ protected void createActions(@NotNull final VBox root) { /** * Save new settings. */ + @FXThread private void processOk() { int needRestart = 0; @@ -1458,11 +1517,13 @@ private void processOk() { } @Override + @FromAnyThread protected @NotNull String getTitleText() { return Messages.SETTINGS_DIALOG_TITLE; } @Override + @FromAnyThread protected @NotNull Point getSize() { return DIALOG_SIZE; } diff --git a/src/main/java/com/ss/editor/ui/dialog/about/AboutDialog.java b/src/main/java/com/ss/editor/ui/dialog/about/AboutDialog.java index 2f5ccfa7..5f405d1e 100644 --- a/src/main/java/com/ss/editor/ui/dialog/about/AboutDialog.java +++ b/src/main/java/com/ss/editor/ui/dialog/about/AboutDialog.java @@ -2,6 +2,8 @@ import com.ss.editor.JFXApplication; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.config.Config; import com.ss.editor.ui.Icons; import com.ss.editor.ui.component.creator.FileCreator; @@ -43,7 +45,6 @@ public class AboutDialog extends AbstractSimpleEditorDialog { @NotNull private static final String LIBRARIES; - static { final InputStream iconsResource = FileCreator.class.getResourceAsStream("/credits/icons.txt"); final InputStream librariesResource = FileCreator.class.getResourceAsStream("/credits/libraries.txt"); @@ -52,6 +53,7 @@ public class AboutDialog extends AbstractSimpleEditorDialog { } @Override + @FXThread protected void createContent(@NotNull final VBox root) { super.createContent(root); @@ -116,21 +118,21 @@ protected void createContent(@NotNull final VBox root) { FXUtils.addClassTo(applicationLabel, CSSClasses.ABOUT_DIALOG_TITLE_LABEL); } - @NotNull @Override - protected String getTitleText() { + @FromAnyThread + protected @NotNull String getTitleText() { return Messages.ABOUT_DIALOG_TITLE; } - @NotNull @Override - protected String getButtonOkText() { + @FromAnyThread + protected @NotNull String getButtonOkText() { return Messages.SIMPLE_DIALOG_BUTTON_OK; } - @NotNull @Override - protected Point getSize() { + @FromAnyThread + protected @NotNull Point getSize() { return DIALOG_SIZE; } } diff --git a/src/main/java/com/ss/editor/ui/dialog/asset/BaseAssetEditorDialog.java b/src/main/java/com/ss/editor/ui/dialog/asset/BaseAssetEditorDialog.java new file mode 100644 index 00000000..eba2bf38 --- /dev/null +++ b/src/main/java/com/ss/editor/ui/dialog/asset/BaseAssetEditorDialog.java @@ -0,0 +1,467 @@ +package com.ss.editor.ui.dialog.asset; + +import static com.ss.editor.Messages.ASSET_EDITOR_DIALOG_TITLE; +import static com.ss.rlib.util.ObjectUtils.notNull; +import com.ss.editor.Editor; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; +import com.ss.editor.manager.JMEFilePreviewManager; +import com.ss.editor.manager.JavaFXImageManager; +import com.ss.editor.manager.ResourceManager; +import com.ss.editor.ui.Icons; +import com.ss.editor.ui.css.CSSClasses; +import com.ss.editor.ui.dialog.AbstractSimpleEditorDialog; +import com.ss.editor.util.EditorUtil; +import com.ss.rlib.ui.util.FXUtils; +import com.ss.rlib.util.FileUtils; +import com.ss.rlib.util.StringUtils; +import com.ss.rlib.util.Utils; +import javafx.beans.binding.BooleanBinding; +import javafx.beans.property.ObjectProperty; +import javafx.beans.value.ObservableBooleanValue; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.TextArea; +import javafx.scene.control.TreeItem; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.awt.*; +import java.net.URL; +import java.nio.file.Path; +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * The base implementation of the {@link AbstractSimpleEditorDialog} to choose some asset resource. + * + * @author JavaSaBr + */ +public class BaseAssetEditorDialog extends AbstractSimpleEditorDialog { + + /** + * The dialog size. + */ + @NotNull + protected static final Point DIALOG_SIZE = new Point(-1, -1); + + /** + * The image manager. + */ + @NotNull + protected static final JavaFXImageManager JAVA_FX_IMAGE_MANAGER = JavaFXImageManager.getInstance(); + + /** + * The editor. + */ + @NotNull + protected static final Editor EDITOR = Editor.getInstance(); + + /** + * The function to handle the choose. + */ + @NotNull + protected final Consumer consumer; + + /** + * The function to validate the choose. + */ + @Nullable + private final Function<@NotNull C, @Nullable String> validator; + + /** + * The image preview. + */ + @Nullable + private ImageView imageView; + + /** + * The preview of text files. + */ + @Nullable + private TextArea textView; + + /** + * The label with any warning. + */ + @Nullable + private Label warningLabel; + + public BaseAssetEditorDialog(@NotNull final Consumer consumer) { + this(consumer, null); + } + + public BaseAssetEditorDialog(@NotNull final Consumer consumer, + @Nullable final Function<@NotNull C, @Nullable String> validator) { + this.consumer = consumer; + this.validator = validator; + } + + @Override + @FXThread + protected void createContent(@NotNull final VBox root) { + + final HBox container = new HBox(); + + final Region firstPart = buildFirstPart(container); + firstPart.prefHeightProperty().bind(root.heightProperty()); + firstPart.prefWidthProperty().bind(root.widthProperty().multiply(0.5)); + + final Region secondPart = buildSecondPart(container); + secondPart.prefHeightProperty().bind(root.heightProperty()); + secondPart.prefWidthProperty().bind(root.widthProperty().multiply(0.5)); + + FXUtils.addToPane(firstPart, container); + FXUtils.addToPane(secondPart, container); + FXUtils.addToPane(container, root); + + FXUtils.addClassTo(container, CSSClasses.DEF_HBOX); + FXUtils.addClassTo(root, CSSClasses.ASSET_EDITOR_DIALOG); + } + + /** + * Build the first part of this dialog. + * + * @param container the horizontal container. + * @return the built component. + */ + @FXThread + protected @NotNull Region buildFirstPart(@NotNull final HBox container) { + throw new RuntimeException("unsupported"); + } + + /** + * Get the target object from the asset element. + * + * @param element the asset element. + * @return the target object. + */ + @FXThread + protected @Nullable C getObject(@NotNull final T element) { + throw new RuntimeException("unsupported"); + } + + /** + * Gets consumer. + * + * @return the function for handling the choose. + */ + @FromAnyThread + protected @NotNull Consumer getConsumer() { + return consumer; + } + + /** + * Handle selected element in the tree. + */ + @FXThread + protected void processSelected(@Nullable final TreeItem newValue) { + + final T element = newValue == null ? null : newValue.getValue(); + final String assetPath = element == null ? null : getAssetPath(element); + final Path realFile = element == null ? null : getRealFile(element); + + validate(getWarningLabel(), element); + try { + + if (assetPath != null) { + updatePreview(assetPath); + } else if (realFile != null) { + updatePreview(realFile); + } else { + updatePreview((String) null); + } + + } catch (final Exception e) { + EditorUtil.handleException(LOGGER, this, e); + } + } + + /** + * Try to get an asset path from the element. + * + * @param element the element. + * @return the asset path or null. + */ + @FXThread + protected @Nullable String getAssetPath(@NotNull final T element) { + return null; + } + + /** + * Try to get a real file from the element. + * + * @param element the element. + * @return the real file or null. + */ + @FXThread + protected @Nullable Path getRealFile(@NotNull final T element) { + return null; + } + + /** + * Update the preview of the file. + * + * @param file the file. + */ + @FXThread + private void updatePreview(@NotNull final Path file) { + + final ImageView imageView = getImageView(); + imageView.setVisible(false); + + final TextArea textView = getTextView(); + textView.setVisible(false); + + final int width = (int) imageView.getFitWidth(); + final int height = (int) imageView.getFitHeight(); + + if (JMEFilePreviewManager.isJmeFile(file)) { + + final JMEFilePreviewManager previewManager = JMEFilePreviewManager.getInstance(); + previewManager.show(file, width, height); + + final ImageView sourceView = previewManager.getImageView(); + final ObjectProperty imageProperty = imageView.imageProperty(); + imageProperty.bind(sourceView.imageProperty()); + + imageView.setVisible(true); + + } else if (JavaFXImageManager.isImage(file)) { + + final Image preview = JAVA_FX_IMAGE_MANAGER.getImagePreview(file, width, height); + imageView.setImage(preview); + imageView.setVisible(true); + + } else if (!StringUtils.isEmpty(FileUtils.getExtension(file))) { + + imageView.imageProperty().unbind(); + imageView.setImage(null); + + textView.setText(FileUtils.read(file)); + textView.setVisible(true); + + } else { + imageView.imageProperty().unbind(); + imageView.setImage(null); + } + } + + /** + * Update the preview of the object by the asset path. + * + * @param assetPath the asset path of the object. + */ + @FXThread + private void updatePreview(@Nullable final String assetPath) { + + final ImageView imageView = getImageView(); + imageView.setVisible(false); + + final TextArea textView = getTextView(); + textView.setVisible(false); + + final int width = (int) imageView.getFitWidth(); + final int height = (int) imageView.getFitHeight(); + + if (JMEFilePreviewManager.isJmeFile(assetPath)) { + + final JMEFilePreviewManager previewManager = JMEFilePreviewManager.getInstance(); + previewManager.show(assetPath, width, height); + + final ImageView sourceView = previewManager.getImageView(); + final ObjectProperty imageProperty = imageView.imageProperty(); + imageProperty.bind(sourceView.imageProperty()); + + imageView.setVisible(true); + + } else if (JavaFXImageManager.isImage(assetPath)) { + + final Image preview = JAVA_FX_IMAGE_MANAGER.getImagePreview(assetPath, width, height); + imageView.setImage(preview); + imageView.setVisible(true); + + } else if (assetPath != null && !StringUtils.isEmpty(FileUtils.getExtension(assetPath))) { + + final ResourceManager resourceManager = ResourceManager.getInstance(); + final URL url = resourceManager.tryToFindResource(assetPath); + + String content; + + if (url != null) { + content = Utils.get(url, first -> FileUtils.read(first.openStream())); + } else { + final Path realFile = EditorUtil.getRealFile(assetPath); + content = realFile == null ? "" : FileUtils.read(realFile); + } + + imageView.imageProperty().unbind(); + imageView.setImage(null); + + textView.setText(content); + textView.setVisible(true); + + } else { + imageView.imageProperty().unbind(); + imageView.setImage(null); + } + } + + /** + * Validate the resource element. + * + * @param warningLabel the warning label + * @param element the element. + */ + @FXThread + protected void validate(@NotNull final Label warningLabel, @Nullable final T element) { + + final Function<@NotNull C, @Nullable String> validator = getValidator(); + if (validator == null) return; + + final C object = element == null ? null : getObject(element); + final String message = object == null ? null : validator.apply(object); + + if (message == null) { + warningLabel.setText(StringUtils.EMPTY); + warningLabel.setVisible(false); + } else { + warningLabel.setText(message); + warningLabel.setVisible(true); + } + } + + /** + * Build second part parent. + * + * @param container the container + * @return the parent + */ + @FXThread + protected @NotNull Region buildSecondPart(@NotNull final HBox container) { + + final StackPane previewContainer = new StackPane(); + + imageView = new ImageView(); + imageView.fitHeightProperty().bind(previewContainer.heightProperty().subtract(2)); + imageView.fitWidthProperty().bind(previewContainer.widthProperty().subtract(2)); + + textView = new TextArea(); + textView.setEditable(false); + textView.prefWidthProperty().bind(previewContainer.widthProperty().subtract(2)); + textView.prefHeightProperty().bind(previewContainer.heightProperty().subtract(2)); + + FXUtils.addToPane(imageView, previewContainer); + FXUtils.addToPane(textView, previewContainer); + + FXUtils.addClassTo(previewContainer, CSSClasses.ASSET_EDITOR_DIALOG_PREVIEW_CONTAINER); + FXUtils.addClassTo(textView, CSSClasses.TRANSPARENT_TEXT_AREA); + + return previewContainer; + } + + /** + * @return the image preview. + */ + @FXThread + protected @NotNull ImageView getImageView() { + return notNull(imageView); + } + + /** + * @return the text preview. + */ + @FXThread + protected @NotNull TextArea getTextView() { + return notNull(textView); + } + + /** + * Gets validator. + * + * @return the function for validating the choose. + */ + @FXThread + protected @Nullable Function<@NotNull C, @Nullable String> getValidator() { + return validator; + } + + /** + * @return the label with any warning. + */ + @FXThread + protected @NotNull Label getWarningLabel() { + return notNull(warningLabel); + } + + @Override + @FXThread + public void hide() { + + final JMEFilePreviewManager previewManager = JMEFilePreviewManager.getInstance(); + previewManager.clear(); + + super.hide(); + } + + @Override + @FXThread + protected void createBeforeActions(@NotNull final HBox container) { + super.createBeforeActions(container); + + warningLabel = new Label(); + warningLabel.setGraphic(new ImageView(Icons.WARNING_24)); + warningLabel.setVisible(false); + + FXUtils.addClassTo(warningLabel, CSSClasses.DIALOG_LABEL_WARNING); + FXUtils.addToPane(warningLabel, container); + } + + @Override + @FXThread + protected void createActions(@NotNull final VBox root) { + super.createActions(root); + + final Button okButton = notNull(getOkButton()); + okButton.disableProperty().bind(buildDisableCondition()); + } + + /** + * Build disable condition. + * + * @return the disable condition. + */ + @FXThread + protected @NotNull BooleanBinding buildDisableCondition() { + final Label warningLabel = getWarningLabel(); + return warningLabel.visibleProperty().or(buildAdditionalDisableCondition()); + } + + /** + * Build additional disable condition. + * + * @return the additional condition. + */ + @FXThread + protected @NotNull ObservableBooleanValue buildAdditionalDisableCondition() { + throw new RuntimeException("unsupported"); + } + + @Override + @FromAnyThread + protected @NotNull Point getSize() { + return DIALOG_SIZE; + } + + @Override + @FromAnyThread + protected @NotNull String getTitleText() { + return ASSET_EDITOR_DIALOG_TITLE; + } +} diff --git a/src/main/java/com/ss/editor/ui/dialog/asset/AssetEditorDialog.java b/src/main/java/com/ss/editor/ui/dialog/asset/file/AssetEditorDialog.java similarity index 95% rename from src/main/java/com/ss/editor/ui/dialog/asset/AssetEditorDialog.java rename to src/main/java/com/ss/editor/ui/dialog/asset/file/AssetEditorDialog.java index 385d4264..2c0df4da 100644 --- a/src/main/java/com/ss/editor/ui/dialog/asset/AssetEditorDialog.java +++ b/src/main/java/com/ss/editor/ui/dialog/asset/file/AssetEditorDialog.java @@ -1,4 +1,4 @@ -package com.ss.editor.ui.dialog.asset; +package com.ss.editor.ui.dialog.asset.file; import static com.ss.editor.Messages.ASSET_EDITOR_DIALOG_TITLE; import static com.ss.editor.ui.component.asset.tree.resource.ResourceElementFactory.createFor; @@ -6,6 +6,8 @@ import static com.ss.rlib.util.ObjectUtils.notNull; import com.ss.editor.Editor; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.config.EditorConfig; import com.ss.editor.manager.ExecutorManager; import com.ss.editor.manager.JMEFilePreviewManager; @@ -61,7 +63,7 @@ public class AssetEditorDialog extends EditorDialog { /** - * The constant DIALOG_SIZE. + * The dialog size. */ @NotNull protected static final Point DIALOG_SIZE = new Point(-1, -1); @@ -90,12 +92,21 @@ public class AssetEditorDialog extends EditorDialog { @NotNull protected static final Editor EDITOR = Editor.getInstance(); + /** + * The handler created files events. + */ @NotNull private final EventHandler createdFileHandler = event -> processEvent((CreatedFileEvent) event); + /** + * The handler selected file events. + */ @NotNull private final EventHandler selectFileHandle = event -> processEvent((RequestSelectFileEvent) event); + /** + * The handler deleted file events, + */ @NotNull private final EventHandler deletedFileHandler = event -> processEvent((DeletedFileEvent) event); @@ -127,13 +138,13 @@ public class AssetEditorDialog extends EditorDialog { * The image preview. */ @Nullable - ImageView imageView; + protected ImageView imageView; /** * The preview of text files. */ @Nullable - TextArea textView; + protected TextArea textView; /** * The label with any warning. @@ -147,21 +158,10 @@ public class AssetEditorDialog extends EditorDialog { @Nullable protected Button okButton; - /** - * Instantiates a new Asset editor dialog. - * - * @param consumer the consumer - */ public AssetEditorDialog(@NotNull final Consumer consumer) { this(consumer, null); } - /** - * Instantiates a new Asset editor dialog. - * - * @param consumer the consumer - * @param validator the validator - */ public AssetEditorDialog(@NotNull final Consumer consumer, @Nullable final Function validator) { this.waitedFilesToSelect = ArrayFactory.newArray(Path.class); this.consumer = consumer; @@ -173,6 +173,7 @@ public AssetEditorDialog(@NotNull final Consumer consumer, @Nullable final Fu * * @param extensionFilter the list of available extensions. */ + @FromAnyThread public void setExtensionFilter(@NotNull final Array extensionFilter) { getResourceTree().setExtensionFilter(extensionFilter); } @@ -182,6 +183,7 @@ public void setExtensionFilter(@NotNull final Array extensionFilter) { * * @param actionTester the action tester. */ + @FromAnyThread public void setActionTester(@Nullable final Predicate> actionTester) { getResourceTree().setActionTester(actionTester); } @@ -191,11 +193,13 @@ public void setActionTester(@Nullable final Predicate> actionTester) { * * @param onlyFolders true if need to show only folders. */ - void setOnlyFolders(final boolean onlyFolders) { + @FromAnyThread + public void setOnlyFolders(final boolean onlyFolders) { getResourceTree().setOnlyFolders(onlyFolders); } @Override + @FXThread protected void createContent(@NotNull final VBox root) { final HBox container = new HBox(); @@ -227,6 +231,7 @@ protected void createContent(@NotNull final VBox root) { * @param container the container * @return the parent */ + @FXThread protected @NotNull Region buildSecondPart(@NotNull final HBox container) { final StackPane previewContainer = new StackPane(); @@ -254,6 +259,7 @@ protected void createContent(@NotNull final VBox root) { * * @return the ok button. */ + @FXThread public @NotNull Button getOkButton() { return notNull(okButton); } @@ -263,10 +269,12 @@ protected void createContent(@NotNull final VBox root) { * * @param element the element */ + @FXThread protected void processOpen(@NotNull final ResourceElement element) { hide(); } + @FXThread private void processKeyEvent(@NotNull final KeyEvent event) { final Button okButton = getOkButton(); if (event.getCode() == KeyCode.ENTER && !okButton.isDisable()) { @@ -275,6 +283,7 @@ private void processKeyEvent(@NotNull final KeyEvent event) { } @Override + @FXThread public void show(@NotNull final Window owner) { super.show(owner); @@ -295,6 +304,7 @@ public void show(@NotNull final Window owner) { /** * Handle creating file event. */ + @FXThread private void processEvent(@NotNull final CreatedFileEvent event) { final Path file = event.getFile(); @@ -312,6 +322,7 @@ private void processEvent(@NotNull final CreatedFileEvent event) { /** * Handle deleting file event. */ + @FXThread private void processEvent(@NotNull final DeletedFileEvent event) { final Path file = event.getFile(); @@ -323,6 +334,7 @@ private void processEvent(@NotNull final DeletedFileEvent event) { /** * Handle selecting file event. */ + @FXThread private void processEvent(@NotNull final RequestSelectFileEvent event) { final Path file = event.getFile(); @@ -342,6 +354,7 @@ private void processEvent(@NotNull final RequestSelectFileEvent event) { /** * @return the list of waited files to select. */ + @FromAnyThread private @NotNull Array getWaitedFilesToSelect() { return waitedFilesToSelect; } @@ -349,6 +362,7 @@ private void processEvent(@NotNull final RequestSelectFileEvent event) { /** * @return the image preview. */ + @FXThread private @NotNull ImageView getImageView() { return notNull(imageView); } @@ -356,6 +370,7 @@ private void processEvent(@NotNull final RequestSelectFileEvent event) { /** * @return the text preview. */ + @FXThread private @NotNull TextArea getTextView() { return notNull(textView); } @@ -365,6 +380,7 @@ private void processEvent(@NotNull final RequestSelectFileEvent event) { * * @return the function for validating the choose. */ + @FromAnyThread protected @Nullable Function getValidator() { return validator; } @@ -372,6 +388,7 @@ private void processEvent(@NotNull final RequestSelectFileEvent event) { /** * @return the label with any warning. */ + @FXThread private @NotNull Label getWarningLabel() { return notNull(warningLabel); } @@ -379,6 +396,7 @@ private void processEvent(@NotNull final RequestSelectFileEvent event) { /** * Handle selected element in the tree. */ + @FXThread private void processSelected(@Nullable final TreeItem newValue) { final ResourceElement element = newValue == null ? null : newValue.getValue(); @@ -398,6 +416,7 @@ private void processSelected(@Nullable final TreeItem newValue) * * @param file the file for preview or null. */ + @FXThread private void updatePreview(@Nullable final Path file) { final ImageView imageView = getImageView(); @@ -442,6 +461,7 @@ private void updatePreview(@Nullable final Path file) { } @Override + @FXThread public void hide() { FX_EVENT_MANAGER.removeEventHandler(CreatedFileEvent.EVENT_TYPE, createdFileHandler); @@ -460,10 +480,12 @@ public void hide() { * @param warningLabel the warning label * @param element the element. */ + @FXThread protected void validate(@NotNull final Label warningLabel, @Nullable final ResourceElement element) { } @Override + @FXThread protected void createActions(@NotNull final VBox root) { final HBox container = new HBox(); @@ -495,6 +517,7 @@ protected void createActions(@NotNull final VBox root) { * * @return the boolean binding */ + @FXThread protected @NotNull BooleanBinding buildDisableCondition() { final ResourceTree resourceTree = getResourceTree(); @@ -510,6 +533,7 @@ protected void createActions(@NotNull final VBox root) { * * @return the function for handling the choose. */ + @FromAnyThread protected @NotNull Consumer getConsumer() { return consumer; } @@ -517,6 +541,7 @@ protected void createActions(@NotNull final VBox root) { /** * @return the tree with all resources. */ + @FXThread private @NotNull ResourceTree getResourceTree() { return notNull(resourceTree); } @@ -524,6 +549,7 @@ protected void createActions(@NotNull final VBox root) { /** * The process of choosing the element. */ + @FXThread private void processSelect() { final ResourceTree resourceTree = getResourceTree(); @@ -539,11 +565,13 @@ private void processSelect() { } @Override + @FromAnyThread protected @NotNull String getTitleText() { return ASSET_EDITOR_DIALOG_TITLE; } @Override + @FromAnyThread protected @NotNull Point getSize() { return DIALOG_SIZE; } diff --git a/src/main/java/com/ss/editor/ui/dialog/asset/FileAssetEditorDialog.java b/src/main/java/com/ss/editor/ui/dialog/asset/file/FileAssetEditorDialog.java similarity index 94% rename from src/main/java/com/ss/editor/ui/dialog/asset/FileAssetEditorDialog.java rename to src/main/java/com/ss/editor/ui/dialog/asset/file/FileAssetEditorDialog.java index 4981d366..d38bab17 100644 --- a/src/main/java/com/ss/editor/ui/dialog/asset/FileAssetEditorDialog.java +++ b/src/main/java/com/ss/editor/ui/dialog/asset/file/FileAssetEditorDialog.java @@ -1,6 +1,7 @@ -package com.ss.editor.ui.dialog.asset; +package com.ss.editor.ui.dialog.asset.file; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.component.asset.tree.resource.FolderResourceElement; import com.ss.editor.ui.component.asset.tree.resource.ResourceElement; import javafx.scene.control.Label; @@ -27,6 +28,7 @@ public FileAssetEditorDialog(@NotNull final Consumer consumer, @Nullable f } @Override + @FXThread protected void processOpen(@NotNull final ResourceElement element) { super.processOpen(element); final Consumer consumer = getConsumer(); @@ -34,6 +36,7 @@ protected void processOpen(@NotNull final ResourceElement element) { } @Override + @FXThread protected void validate(@NotNull final Label warningLabel, @Nullable final ResourceElement element) { super.validate(warningLabel, element); diff --git a/src/main/java/com/ss/editor/ui/dialog/asset/FolderAssetEditorDialog.java b/src/main/java/com/ss/editor/ui/dialog/asset/file/FolderAssetEditorDialog.java similarity index 93% rename from src/main/java/com/ss/editor/ui/dialog/asset/FolderAssetEditorDialog.java rename to src/main/java/com/ss/editor/ui/dialog/asset/file/FolderAssetEditorDialog.java index faa9c89f..a04610f2 100644 --- a/src/main/java/com/ss/editor/ui/dialog/asset/FolderAssetEditorDialog.java +++ b/src/main/java/com/ss/editor/ui/dialog/asset/file/FolderAssetEditorDialog.java @@ -1,5 +1,6 @@ -package com.ss.editor.ui.dialog.asset; +package com.ss.editor.ui.dialog.asset.file; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.component.asset.tree.resource.ResourceElement; import javafx.scene.control.Label; import org.jetbrains.annotations.NotNull; @@ -28,6 +29,7 @@ public FolderAssetEditorDialog(@NotNull final Consumer consumer, } @Override + @FXThread protected void processOpen(@NotNull final ResourceElement element) { super.processOpen(element); final Consumer consumer = getConsumer(); @@ -35,6 +37,7 @@ protected void processOpen(@NotNull final ResourceElement element) { } @Override + @FXThread protected void validate(@NotNull final Label warningLabel, @Nullable final ResourceElement element) { super.validate(warningLabel, element); diff --git a/src/main/java/com/ss/editor/ui/dialog/asset/ParticlesAssetEditorDialog.java b/src/main/java/com/ss/editor/ui/dialog/asset/file/ParticlesAssetEditorDialog.java similarity index 94% rename from src/main/java/com/ss/editor/ui/dialog/asset/ParticlesAssetEditorDialog.java rename to src/main/java/com/ss/editor/ui/dialog/asset/file/ParticlesAssetEditorDialog.java index 0c3ed88a..2e947059 100644 --- a/src/main/java/com/ss/editor/ui/dialog/asset/ParticlesAssetEditorDialog.java +++ b/src/main/java/com/ss/editor/ui/dialog/asset/file/ParticlesAssetEditorDialog.java @@ -1,4 +1,4 @@ -package com.ss.editor.ui.dialog.asset; +package com.ss.editor.ui.dialog.asset.file; import static com.ss.editor.util.EditorUtil.getAssetFile; import static com.ss.editor.util.EditorUtil.toAssetPath; @@ -10,6 +10,7 @@ import com.jme3.material.MaterialDef; import com.jme3.shader.VarType; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.component.asset.tree.resource.ResourceElement; import com.ss.editor.ui.css.CSSClasses; import com.ss.rlib.ui.util.FXUtils; @@ -61,18 +62,12 @@ public class ParticlesAssetEditorDialog extends AssetEditorDialog consumer) { super(consumer); } - @NotNull @Override - protected Region buildSecondPart(@NotNull final HBox container) { + protected @NotNull Region buildSecondPart(@NotNull final HBox container) { textureParamNameLabel = new Label(Messages.PARTICLE_ASSET_EDITOR_DIALOG_TEXTURE_PARAM_LABEL + ":"); textureParamNameLabel.prefWidthProperty().bind(container.widthProperty().multiply(0.25)); @@ -117,20 +112,21 @@ protected Region buildSecondPart(@NotNull final HBox container) { /** * @return the combo box with texture parameter name. */ - @NotNull - private ComboBox getTextureParamNameComboBox() { + @FXThread + private @NotNull ComboBox getTextureParamNameComboBox() { return notNull(textureParamNameComboBox); } /** * @return the check box for applying the lighting transform. */ - @NotNull - private CheckBox getApplyLightingTransformCheckBox() { + @FXThread + private @NotNull CheckBox getApplyLightingTransformCheckBox() { return notNull(applyLightingTransformCheckBox); } @Override + @FXThread protected void processOpen(@NotNull final ResourceElement element) { super.processOpen(element); @@ -155,9 +151,9 @@ protected void processOpen(@NotNull final ResourceElement element) { consumer.accept(new ParticlesMaterial(material, textureParamName, transformBox.isSelected())); } - @NotNull @Override - protected BooleanBinding buildDisableCondition() { + @FXThread + protected @NotNull BooleanBinding buildDisableCondition() { final ComboBox comboBox = getTextureParamNameComboBox(); final SingleSelectionModel selectionModel = comboBox.getSelectionModel(); return super.buildDisableCondition().or(selectionModel.selectedItemProperty() @@ -165,6 +161,7 @@ protected BooleanBinding buildDisableCondition() { } @Override + @FXThread protected void validate(@NotNull final Label warningLabel, @Nullable final ResourceElement element) { final ComboBox comboBox = getTextureParamNameComboBox(); diff --git a/src/main/java/com/ss/editor/ui/dialog/asset/virtual/StringVirtualAssetEditorDialog.java b/src/main/java/com/ss/editor/ui/dialog/asset/virtual/StringVirtualAssetEditorDialog.java new file mode 100644 index 00000000..4b24fe7f --- /dev/null +++ b/src/main/java/com/ss/editor/ui/dialog/asset/virtual/StringVirtualAssetEditorDialog.java @@ -0,0 +1,47 @@ +package com.ss.editor.ui.dialog.asset.virtual; + +import com.ss.editor.annotation.FromAnyThread; +import com.ss.rlib.util.FileUtils; +import com.ss.rlib.util.StringUtils; +import com.ss.rlib.util.array.Array; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * The implementation to work with string resources. + * + * @author JavaSaBr + */ +public class StringVirtualAssetEditorDialog extends VirtualAssetEditorDialog { + + private static final Function<@NotNull String, @Nullable String> DEFAULT_VALIDATOR = s -> { + + final String extension = FileUtils.getExtension(s); + + if (StringUtils.isEmpty(extension)) { + return "You need to select a file"; + } + + return null; + }; + + public StringVirtualAssetEditorDialog(@NotNull final Consumer consumer, + @NotNull final Array resources) { + this(consumer, DEFAULT_VALIDATOR, resources); + } + + public StringVirtualAssetEditorDialog(@NotNull final Consumer consumer, + @Nullable final Function<@NotNull String, @Nullable String> validator, + @NotNull final Array resources) { + super(consumer, validator, resources); + } + + @Override + @FromAnyThread + protected @NotNull Class getObjectsType() { + return String.class; + } +} diff --git a/src/main/java/com/ss/editor/ui/dialog/asset/virtual/VirtualAssetEditorDialog.java b/src/main/java/com/ss/editor/ui/dialog/asset/virtual/VirtualAssetEditorDialog.java new file mode 100644 index 00000000..74c5bb23 --- /dev/null +++ b/src/main/java/com/ss/editor/ui/dialog/asset/virtual/VirtualAssetEditorDialog.java @@ -0,0 +1,172 @@ +package com.ss.editor.ui.dialog.asset.virtual; + +import static com.ss.rlib.util.ObjectUtils.notNull; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; +import com.ss.editor.ui.component.virtual.tree.VirtualResourceTree; +import com.ss.editor.ui.component.virtual.tree.resource.RootVirtualResourceElement; +import com.ss.editor.ui.component.virtual.tree.resource.VirtualResourceElement; +import com.ss.editor.ui.component.virtual.tree.resource.VirtualResourceElementFactory; +import com.ss.editor.ui.dialog.asset.BaseAssetEditorDialog; +import com.ss.rlib.util.array.Array; +import javafx.beans.binding.Bindings; +import javafx.beans.binding.BooleanBinding; +import javafx.beans.property.ReadOnlyObjectProperty; +import javafx.beans.value.ObservableBooleanValue; +import javafx.scene.control.MultipleSelectionModel; +import javafx.scene.control.TreeItem; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import javafx.stage.Window; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * The implementation of the {@link BaseAssetEditorDialog} to choose the object from a virtual asset. + * + * @param the type parameter + * @author JavaSaBr + */ +public class VirtualAssetEditorDialog extends BaseAssetEditorDialog, C> { + + /** + * The all resources. + */ + @NotNull + private final Array resources; + + /** + * The tree with all resources. + */ + @Nullable + private VirtualResourceTree resourceTree; + + public VirtualAssetEditorDialog(@NotNull final Consumer consumer, @NotNull final Array resources) { + this(consumer, null, resources); + } + + public VirtualAssetEditorDialog(@NotNull final Consumer consumer, + @Nullable final Function<@NotNull C, @Nullable String> validator, + @NotNull final Array resources) { + super(consumer, validator); + this.resources = resources; + } + + /** + * @see VirtualResourceTree#setPathFunction(Function) + */ + @FromAnyThread + public void setPathFunction(@Nullable final Function pathFunction) { + getResourceTree().setPathFunction(pathFunction); + } + + @Override + @FXThread + protected @Nullable String getAssetPath(@NotNull final VirtualResourceElement element) { + return getResourceTree().getPath(element.getObject()); + } + + @Override + @FXThread + protected @NotNull Region buildFirstPart(@NotNull final HBox container) { + + resourceTree = new VirtualResourceTree<>(getObjectsType()); + resourceTree.getSelectionModel().selectedItemProperty() + .addListener((observable, oldValue, newValue) -> processSelected(newValue)); + + return resourceTree; + } + + /** + * Get the type of presented objects. + * + * @return the type of presented objects. + */ + @FromAnyThread + protected @NotNull Class getObjectsType() { + throw new RuntimeException("unsupported"); + } + + @Override + @FXThread + public void show(@NotNull final Window owner) { + super.show(owner); + + final VirtualResourceTree resourceTree = getResourceTree(); + final RootVirtualResourceElement newRoot = + VirtualResourceElementFactory.build(resources, resourceTree); + + resourceTree.fill(newRoot); + resourceTree.expandAll(); + + EXECUTOR_MANAGER.addFXTask(resourceTree::requestFocus); + } + + @Override + @FXThread + protected @Nullable C getObject(@NotNull final VirtualResourceElement element) { + final Object object = element.getObject(); + final Class type = getObjectsType(); + return type.isInstance(object) ? type.cast(object) : null; + } + + /** + * @return the tree with all resources. + */ + @FXThread + private @NotNull VirtualResourceTree getResourceTree() { + return notNull(resourceTree); + } + + @Override + @FXThread + protected @NotNull ObservableBooleanValue buildAdditionalDisableCondition() { + + final VirtualResourceTree resourceTree = getResourceTree(); + final MultipleSelectionModel>> selectionModel = resourceTree.getSelectionModel(); + final ReadOnlyObjectProperty>> selectedItemProperty = selectionModel.selectedItemProperty(); + + final Class type = getObjectsType(); + final BooleanBinding typeCondition = new BooleanBinding() { + + @Override + protected boolean computeValue() { + final TreeItem> treeItem = selectedItemProperty.get(); + return treeItem == null || !type.isInstance(treeItem.getValue().getObject()); + } + + @Override + public Boolean getValue() { + return computeValue(); + } + }; + + return Bindings.or(selectedItemProperty.isNull(), typeCondition); + } + + @Override + @FXThread + protected void processOk() { + super.processOk(); + + final VirtualResourceTree resourceTree = getResourceTree(); + final MultipleSelectionModel>> selectionModel = resourceTree.getSelectionModel(); + final TreeItem> selectedItem = selectionModel.getSelectedItem(); + + if (selectedItem == null) { + hide(); + return; + } + + final VirtualResourceElement element = selectedItem.getValue(); + final Object object = element.getObject(); + final Class type = getObjectsType(); + + if (type.isInstance(object)) { + getConsumer().accept(type.cast(object)); + } + } +} diff --git a/src/main/java/com/ss/editor/ui/dialog/converter/ModelConverterDialog.java b/src/main/java/com/ss/editor/ui/dialog/converter/ModelConverterDialog.java index 741ed2a6..300d43bb 100644 --- a/src/main/java/com/ss/editor/ui/dialog/converter/ModelConverterDialog.java +++ b/src/main/java/com/ss/editor/ui/dialog/converter/ModelConverterDialog.java @@ -2,6 +2,8 @@ import static com.ss.rlib.util.ObjectUtils.notNull; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.control.choose.ChooseFolderControl; import com.ss.editor.ui.css.CSSClasses; import com.ss.editor.ui.dialog.AbstractSimpleEditorDialog; @@ -66,13 +68,6 @@ public class ModelConverterDialog extends AbstractSimpleEditorDialog { @Nullable private CheckBox overwriteMaterialsCheckBox; - /** - * Instantiates a new Model converter dialog. - * - * @param source the source - * @param destination the destination - * @param callback the callback - */ public ModelConverterDialog(@NotNull final Path source, @NotNull final Path destination, @NotNull final Consumer callback) { this.callback = callback; @@ -81,6 +76,7 @@ public ModelConverterDialog(@NotNull final Path source, @NotNull final Path dest } @Override + @FXThread protected void createContent(@NotNull final GridPane root) { super.createContent(root); @@ -136,6 +132,7 @@ protected void createContent(@NotNull final GridPane root) { } @Override + @FromAnyThread protected boolean isGridStructure() { return true; } @@ -143,6 +140,7 @@ protected boolean isGridStructure() { /** * Validate. */ + @FXThread private void validate() { final Button okButton = getOkButton(); @@ -173,8 +171,8 @@ private void validate() { /** * @return the export materials check box. */ - @NotNull - private CheckBox getExportMaterialsCheckBox() { + @FXThread + private @NotNull CheckBox getExportMaterialsCheckBox() { return notNull(exportMaterialsCheckBox); } @@ -183,6 +181,7 @@ private CheckBox getExportMaterialsCheckBox() { * * @return true if need to export materials. */ + @FXThread public boolean isExportMaterials() { return getExportMaterialsCheckBox().isSelected(); } @@ -190,8 +189,8 @@ public boolean isExportMaterials() { /** * @return the overwrite materials check box. */ - @NotNull - private CheckBox getOverwriteMaterialsCheckBox() { + @FXThread + private @NotNull CheckBox getOverwriteMaterialsCheckBox() { return notNull(overwriteMaterialsCheckBox); } @@ -200,6 +199,7 @@ private CheckBox getOverwriteMaterialsCheckBox() { * * @return true if we can overwrite materials. */ + @FXThread public boolean isOverwriteMaterials() { return getOverwriteMaterialsCheckBox().isSelected(); } @@ -207,8 +207,8 @@ public boolean isOverwriteMaterials() { /** * @return the destination folder control. */ - @NotNull - private ChooseFolderControl getDestinationControl() { + @FXThread + private @NotNull ChooseFolderControl getDestinationControl() { return notNull(destinationControl); } @@ -217,16 +217,16 @@ private ChooseFolderControl getDestinationControl() { * * @return the destination folder. */ - @NotNull - public Path getDestinationFolder() { + @FXThread + public @NotNull Path getDestinationFolder() { return notNull(getDestinationControl().getFolder()); } /** * @return the materials destination folder control. */ - @NotNull - private ChooseFolderControl getMaterialsFolderControl() { + @FXThread + private @NotNull ChooseFolderControl getMaterialsFolderControl() { return notNull(materialsFolderControl); } @@ -235,16 +235,16 @@ private ChooseFolderControl getMaterialsFolderControl() { * * @return the materials destination folder. */ - @NotNull - public Path getMaterialsFolder() { + @FXThread + public @NotNull Path getMaterialsFolder() { return notNull(getMaterialsFolderControl().getFolder()); } /** * @return the filename field. */ - @NotNull - private TextField getFilenameField() { + @FXThread + private @NotNull TextField getFilenameField() { return notNull(filenameField); } @@ -253,40 +253,41 @@ private TextField getFilenameField() { * * @return the filename. */ - @NotNull - public String getFilename() { + @FXThread + public @NotNull String getFilename() { return getFilenameField().getText(); } /** * @return the callback. */ - @NotNull - private Consumer getCallback() { + @FXThread + private @NotNull Consumer getCallback() { return callback; } @Override + @FXThread protected void processOk() { super.processOk(); getCallback().accept(this); } - @NotNull @Override - protected String getTitleText() { + @FromAnyThread + protected @NotNull String getTitleText() { return Messages.MODEL_CONVERTER_DIALOG_TITLE; } - @NotNull @Override - protected String getButtonOkText() { + @FromAnyThread + protected @NotNull String getButtonOkText() { return Messages.MODEL_CONVERTER_DIALOG_BUTTON_OK; } - @NotNull @Override - protected Point getSize() { + @FromAnyThread + protected @NotNull Point getSize() { return DIALOG_SIZE; } } diff --git a/src/main/java/com/ss/editor/ui/dialog/folder/OpenExternalFolderEditorDialog.java b/src/main/java/com/ss/editor/ui/dialog/folder/OpenExternalFolderEditorDialog.java index 99f3daba..50d1b910 100644 --- a/src/main/java/com/ss/editor/ui/dialog/folder/OpenExternalFolderEditorDialog.java +++ b/src/main/java/com/ss/editor/ui/dialog/folder/OpenExternalFolderEditorDialog.java @@ -4,6 +4,7 @@ import com.ss.editor.Editor; import com.ss.editor.Messages; import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.manager.ExecutorManager; import com.ss.editor.ui.component.asset.tree.ResourceTree; import com.ss.editor.ui.component.asset.tree.resource.ResourceElement; @@ -72,6 +73,7 @@ public OpenExternalFolderEditorDialog(@NotNull final Consumer<@NotNull Path> con } @Override + @FXThread protected void createContent(@NotNull final VBox root) { resourceTree = new ResourceTree(this::processOpen, false); @@ -111,6 +113,7 @@ protected void processOk() { } @Override + @FXThread public void show(@NotNull final Window owner) { super.show(owner); @@ -136,6 +139,7 @@ public void show(@NotNull final Window owner) { /** * Handle selected element in the tree. */ + @FXThread private void processSelected(@Nullable final TreeItem newValue) { final ResourceElement element = newValue == null ? null : newValue.getValue(); final Path file = element == null ? null : element.getFile(); @@ -148,6 +152,7 @@ private void processSelected(@Nullable final TreeItem newValue) * * @return the function for handling the choose. */ + @FXThread protected @NotNull Consumer<@NotNull Path> getConsumer() { return consumer; } @@ -157,6 +162,7 @@ private void processSelected(@Nullable final TreeItem newValue) * * @param initDirectory the init directory. */ + @FXThread public void setInitDirectory(@Nullable final Path initDirectory) { this.initDirectory = initDirectory; } @@ -166,6 +172,7 @@ public void setInitDirectory(@Nullable final Path initDirectory) { * * @return the init directory. */ + @FXThread public @Nullable Path getInitDirectory() { return initDirectory; } @@ -173,16 +180,19 @@ public void setInitDirectory(@Nullable final Path initDirectory) { /** * @return the tree with all resources. */ + @FXThread private @NotNull ResourceTree getResourceTree() { return notNull(resourceTree); } @Override + @FromAnyThread protected @NotNull Point getSize() { return DIALOG_SIZE; } @Override + @FromAnyThread protected @NotNull String getButtonOkText() { return Messages.SIMPLE_DIALOG_BUTTON_SELECT; } diff --git a/src/main/java/com/ss/editor/ui/dialog/plugin/PluginsDialog.java b/src/main/java/com/ss/editor/ui/dialog/plugin/PluginsDialog.java index ff48fd95..ef916f3b 100644 --- a/src/main/java/com/ss/editor/ui/dialog/plugin/PluginsDialog.java +++ b/src/main/java/com/ss/editor/ui/dialog/plugin/PluginsDialog.java @@ -5,6 +5,8 @@ import com.ss.editor.Messages; import com.ss.editor.analytics.google.GAEvent; import com.ss.editor.analytics.google.GAnalytics; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.manager.PluginManager; import com.ss.editor.plugin.EditorPlugin; import com.ss.editor.ui.FXConstants; @@ -64,6 +66,7 @@ public PluginsDialog() { } @Override + @FXThread protected void createContent(@NotNull final VBox root) { super.createContent(root); @@ -104,23 +107,24 @@ protected void createContent(@NotNull final VBox root) { FXUtils.addClassTo(root, CSSClasses.PLUGINS_DIALOG); } - @NotNull @Override - protected String getTitleText() { + @FromAnyThread + protected @NotNull String getTitleText() { return Messages.PLUGINS_DIALOG_TITLE; } /** * @return the list of installed plugins. */ - @NotNull - private ListView getPluginListView() { + @FXThread + private @NotNull ListView getPluginListView() { return notNull(pluginListView); } /** * Refresh the list of installed plugins. */ + @FXThread private void refreshPlugins() { final ObservableList items = pluginListView.getItems(); @@ -132,6 +136,7 @@ private void refreshPlugins() { /** * Process remove. */ + @FXThread private void processRemove() { final ListView pluginListView = getPluginListView(); @@ -150,12 +155,13 @@ private void processRemove() { /** * @return the list of original plugin ids. */ - @NotNull - private Array getOriginalIds() { + @FXThread + private @NotNull Array getOriginalIds() { return originalIds; } @Override + @FXThread protected void processClose() { super.processClose(); @@ -179,6 +185,7 @@ protected void processClose() { /** * Process install a plugin. */ + @FXThread private void processAdd() { GAnalytics.sendPageView("PluginChooseDialog", null, "/dialog/PluginChooseDialog"); @@ -201,19 +208,20 @@ private void processAdd() { } @Override + @FromAnyThread protected boolean needOkButton() { return false; } - @NotNull @Override - protected String getButtonCloseText() { + @FromAnyThread + protected @NotNull String getButtonCloseText() { return Messages.SIMPLE_DIALOG_BUTTON_CLOSE; } - @NotNull @Override - protected Point getSize() { + @FromAnyThread + protected @NotNull Point getSize() { return DIALOG_SIZE; } } diff --git a/src/main/java/com/ss/editor/ui/dialog/save/SaveAsEditorDialog.java b/src/main/java/com/ss/editor/ui/dialog/save/SaveAsEditorDialog.java index f278b495..f12b805c 100644 --- a/src/main/java/com/ss/editor/ui/dialog/save/SaveAsEditorDialog.java +++ b/src/main/java/com/ss/editor/ui/dialog/save/SaveAsEditorDialog.java @@ -117,6 +117,7 @@ public SaveAsEditorDialog(@NotNull final Consumer<@NotNull Path> consumer) { * * @return the title of filename field. */ + @FXThread protected @NotNull String getFileNameLabelText() { return SAVE_AS_EDITOR_DIALOG_FIELD_FILENAME; } @@ -126,6 +127,7 @@ public SaveAsEditorDialog(@NotNull final Consumer<@NotNull Path> consumer) { * * @param extension the target file extension. */ + @FXThread public void setExtension(@NotNull final String extension) { this.extension = extension; getResourceTree().setExtensionFilter(ArrayFactory.asArray(extension)); @@ -136,6 +138,7 @@ public void setExtension(@NotNull final String extension) { * * @return the target file extension. */ + @FXThread private @NotNull String getExtension() { return extension == null ? "" : extension; } @@ -145,11 +148,13 @@ public void setExtension(@NotNull final String extension) { * * @param actionTester the action tester. */ + @FXThread public void setActionTester(@NotNull final Predicate<@NotNull Class> actionTester) { getResourceTree().setActionTester(actionTester); } @Override + @FXThread protected void createContent(@NotNull final VBox root) { super.createContent(root); @@ -205,6 +210,7 @@ protected void createSettings(@NotNull final GridPane root) { * * @param newValue the new selected item. */ + @FXThread protected void processSelection(@Nullable final TreeItem newValue) { if (newValue != null) { @@ -284,11 +290,13 @@ protected void validateFileName() { * * @return the filename field. */ + @FXThread protected @NotNull TextField getFileNameField() { return notNull(fileNameField); } @Override + @FXThread public void show(@NotNull final Window owner) { super.show(owner); @@ -317,6 +325,7 @@ private void expand(@NotNull final Path file, @NotNull final ResourceTree resour /** * Handle creating file event. */ + @FXThread private void processEvent(@NotNull final CreatedFileEvent event) { final Path file = event.getFile(); @@ -334,6 +343,7 @@ private void processEvent(@NotNull final CreatedFileEvent event) { /** * Handle deleting file event. */ + @FXThread private void processEvent(@NotNull final DeletedFileEvent event) { final Path file = event.getFile(); @@ -345,6 +355,7 @@ private void processEvent(@NotNull final DeletedFileEvent event) { /** * Handle selecting file event. */ + @FXThread private void processEvent(@NotNull final RequestSelectFileEvent event) { final Path file = event.getFile(); @@ -364,11 +375,13 @@ private void processEvent(@NotNull final RequestSelectFileEvent event) { /** * @return the list of waited files to select. */ + @FXThread private @NotNull Array getWaitedFilesToSelect() { return waitedFilesToSelect; } @Override + @FXThread public void hide() { FX_EVENT_MANAGER.removeEventHandler(CreatedFileEvent.EVENT_TYPE, createdFileHandler); FX_EVENT_MANAGER.removeEventHandler(RequestSelectFileEvent.EVENT_TYPE, selectFileHandle); @@ -381,6 +394,7 @@ public void hide() { * * @return the function for handling the choose. */ + @FXThread protected @NotNull Consumer<@NotNull Path> getConsumer() { return consumer; } @@ -388,21 +402,25 @@ public void hide() { /** * @return the tree with all resources. */ + @FXThread private @NotNull ResourceTree getResourceTree() { return notNull(resourceTree); } @Override + @FromAnyThread protected @NotNull String getTitleText() { return SAVE_AS_EDITOR_DIALOG_TITLE; } @Override + @FromAnyThread protected @NotNull Point getSize() { return DIALOG_SIZE; } @Override + @FromAnyThread protected @NotNull String getButtonOkText() { return Messages.SIMPLE_DIALOG_BUTTON_SAVE; } diff --git a/src/main/java/com/ss/editor/ui/util/UIUtils.java b/src/main/java/com/ss/editor/ui/util/UIUtils.java index 7318be16..9f708bcb 100644 --- a/src/main/java/com/ss/editor/ui/util/UIUtils.java +++ b/src/main/java/com/ss/editor/ui/util/UIUtils.java @@ -7,9 +7,10 @@ import com.ss.editor.annotation.FXThread; import com.ss.editor.model.UObject; import com.ss.editor.ui.component.ScreenComponent; -import com.ss.editor.ui.dialog.asset.AssetEditorDialog; -import com.ss.editor.ui.dialog.asset.FileAssetEditorDialog; -import com.ss.editor.ui.dialog.asset.FolderAssetEditorDialog; +import com.ss.editor.ui.dialog.asset.file.AssetEditorDialog; +import com.ss.editor.ui.dialog.asset.file.FileAssetEditorDialog; +import com.ss.editor.ui.dialog.asset.file.FolderAssetEditorDialog; +import com.ss.editor.ui.dialog.asset.virtual.StringVirtualAssetEditorDialog; import com.ss.editor.ui.dialog.save.SaveAsEditorDialog; import com.ss.rlib.util.ClassUtils; import com.ss.rlib.util.FileUtils; @@ -407,6 +408,48 @@ public static void overrideTooltipBehavior(int openDelayInMillis, int visibleDur return container; } + /** + * Visit all items. + * + * @param the type parameter. + * @param item the tree item. + * @param visitor the visitor. + */ + @FXThread + public static void visit(@NotNull final TreeItem item, @NotNull final Consumer> visitor) { + visitor.accept(item); + + final ObservableList> children = item.getChildren(); + if (children.isEmpty()) return; + + for (final TreeItem child : children) { + visit(child, visitor); + } + } + + /** + * Visit all items. + * + * @param the type parameter. + * @param item the tree item. + * @param visitor the visitor. + */ + @FXThread + public static boolean visitUntil(@NotNull final TreeItem item, + @NotNull final Predicate> visitor) { + + if (!visitor.test(item)) return false; + + final ObservableList> children = item.getChildren(); + if (children.isEmpty()) return true; + + for (final TreeItem child : children) { + if (!visitUntil(child, visitor)) return false; + } + + return true; + } + /** * Collect all elements of tree items. * @@ -538,6 +581,20 @@ public static void updateEditedCell(final Labeled cell) { } } + /** + * Open an resource asset dialog. + * + * @param handler the result handler. + * @param resources the resources. + */ + @FXThread + public static void openResourceAssetDialog(@NotNull final Consumer handler, + @NotNull final Array resources) { + + final StringVirtualAssetEditorDialog dialog = new StringVirtualAssetEditorDialog(handler, resources); + dialog.show(); + } + /** * Open an asset dialog. * diff --git a/src/main/java/com/ss/editor/util/EditorUtil.java b/src/main/java/com/ss/editor/util/EditorUtil.java index 953b880f..16783b53 100644 --- a/src/main/java/com/ss/editor/util/EditorUtil.java +++ b/src/main/java/com/ss/editor/util/EditorUtil.java @@ -14,6 +14,7 @@ import com.ss.editor.JFXApplication; import com.ss.editor.analytics.google.GAnalytics; import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.config.EditorConfig; import com.ss.editor.extension.scene.SceneLayer; import com.ss.editor.extension.scene.SceneNode; @@ -189,8 +190,8 @@ public static float getAngle(@NotNull final Vector2f center, @NotNull final Vect * @param path the path to resource. * @return the input stream of the resource or null. */ - @NotNull - public static InputStream getInputStream(@NotNull final String path) { + @FromAnyThread + public static @Nullable InputStream getInputStream(@NotNull final String path) { return JFXApplication.class.getResourceAsStream(path); } @@ -201,8 +202,8 @@ public static InputStream getInputStream(@NotNull final String path) { * @param classLoader the class loader. * @return the input stream of the resource or null. */ - @NotNull - public static InputStream getInputStream(@NotNull final String path, @NotNull final ClassLoader classLoader) { + @FromAnyThread + public static @Nullable InputStream getInputStream(@NotNull final String path, @NotNull final ClassLoader classLoader) { return classLoader.getResourceAsStream(path); } diff --git a/src/main/resources/credits/icons.txt b/src/main/resources/credits/icons.txt index c705d602..b4ce9f99 100644 --- a/src/main/resources/credits/icons.txt +++ b/src/main/resources/credits/icons.txt @@ -79,4 +79,5 @@ ui/icons/svg/draws.svg icon made by http://www.flaticon.com/authors/freepik from ui/icons/svg/scale-symbol.svg icon made by https://www.flaticon.com/authors/popcic from www.flaticon.com ui/icons/svg/font-selection-editor.svg made by https://www.flaticon.com/authors/dave-gandy from www.flaticon.com ui/icons/svg/inbox.svg icon made by http://www.flaticon.com/authors/freepik from www.flaticon.com -ui/icons/svg/debug.svg icon made by http://www.flaticon.com/authors/freepik from www.flaticon.com \ No newline at end of file +ui/icons/svg/debug.svg icon made by http://www.flaticon.com/authors/freepik from www.flaticon.com +ui/icons/filetypes/vector.svg icon made by https://www.flaticon.com/authors/freepik from www.flaticon.com \ No newline at end of file diff --git a/src/main/resources/ui/icons/filetypes/vector.svg b/src/main/resources/ui/icons/filetypes/vector.svg new file mode 100644 index 00000000..b4dd7cc7 --- /dev/null +++ b/src/main/resources/ui/icons/filetypes/vector.svg @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 8e7f081bbeb166831455dc33d01454e900a11279 Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Tue, 12 Sep 2017 14:24:35 +0300 Subject: [PATCH 12/50] added API to skip property builders. --- .../builder/PropertyBuilderFilter.java | 23 +++++++++++ .../builder/PropertyBuilderRegistry.java | 40 +++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 src/main/java/com/ss/editor/ui/control/property/builder/PropertyBuilderFilter.java diff --git a/src/main/java/com/ss/editor/ui/control/property/builder/PropertyBuilderFilter.java b/src/main/java/com/ss/editor/ui/control/property/builder/PropertyBuilderFilter.java new file mode 100644 index 00000000..629865cb --- /dev/null +++ b/src/main/java/com/ss/editor/ui/control/property/builder/PropertyBuilderFilter.java @@ -0,0 +1,23 @@ +package com.ss.editor.ui.control.property.builder; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * The interface to filter builder for some objects. + * + * @author JavaSaBr + */ +@FunctionalInterface +public interface PropertyBuilderFilter { + + /** + * Check the property builder and the object. + * + * @param builder the property builder. + * @param object the object. + * @param parent the parent. + * @return true of we should skip the builder for the object. + */ + boolean skip(@NotNull final PropertyBuilder builder, @NotNull final Object object, @Nullable final Object parent); +} diff --git a/src/main/java/com/ss/editor/ui/control/property/builder/PropertyBuilderRegistry.java b/src/main/java/com/ss/editor/ui/control/property/builder/PropertyBuilderRegistry.java index 3dabd246..96900704 100644 --- a/src/main/java/com/ss/editor/ui/control/property/builder/PropertyBuilderRegistry.java +++ b/src/main/java/com/ss/editor/ui/control/property/builder/PropertyBuilderRegistry.java @@ -1,5 +1,7 @@ package com.ss.editor.ui.control.property.builder; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.model.undo.editor.ChangeConsumer; import com.ss.editor.ui.control.app.state.property.builder.impl.AppStatePropertyBuilder; import com.ss.editor.ui.control.filter.property.builder.impl.FilterPropertyBuilder; @@ -21,15 +23,26 @@ public class PropertyBuilderRegistry { @NotNull private static final PropertyBuilderRegistry INSTANCE = new PropertyBuilderRegistry(); + @FromAnyThread public static @NotNull PropertyBuilderRegistry getInstance() { return INSTANCE; } + /** + * The list of property builders. + */ @NotNull private final Array builders; + /** + * THe list of filters. + */ + @NotNull + private final Array filters; + private PropertyBuilderRegistry() { builders = ArrayFactory.newArray(PropertyBuilder.class); + filters = ArrayFactory.newArray(PropertyBuilderFilter.class); builders.add(AudioNodePropertyBuilder.getInstance()); builders.add(ParticleEmitterPropertyBuilder.getInstance()); builders.add(GeometryPropertyBuilder.getInstance()); @@ -48,15 +61,27 @@ private PropertyBuilderRegistry() { builders.add(Toneg0dParticleInfluencerPropertyBuilder.getInstance()); builders.add(MaterialSettingsPropertyBuilder.getInstance()); } + /** * Register a new property builder. * * @param builder the property builder. */ + @FromAnyThread public void register(@NotNull final PropertyBuilder builder) { builders.add(builder); } + /** + * Register a new property builder filter. + * + * @param filter the property builder filter. + */ + @FromAnyThread + public void register(@NotNull final PropertyBuilderFilter filter) { + filters.add(filter); + } + /** * Build properties controls for the object to the container. * @@ -65,10 +90,25 @@ public void register(@NotNull final PropertyBuilder builder) { * @param container the container for containing these controls. * @param changeConsumer the consumer to work between controls and editor. */ + @FXThread public void buildFor(@NotNull final Object object, @Nullable final Object parent, @NotNull final VBox container, @NotNull final ChangeConsumer changeConsumer) { for (final PropertyBuilder builder : builders) { + + boolean needSkip = false; + + for (final PropertyBuilderFilter filter : filters) { + if (filter.skip(builder, object, parent)) { + needSkip = true; + break; + } + } + + if (needSkip) { + continue; + } + builder.buildFor(object, parent, container, changeConsumer); } } From d20465f8d32bb25c8a99821402bdf26fc1c81367 Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Tue, 12 Sep 2017 17:58:11 +0300 Subject: [PATCH 13/50] refactored asset dialog. --- .../dialog/asset/file/AssetEditorDialog.java | 337 +----------------- .../asset/file/FileAssetEditorDialog.java | 6 + .../asset/file/FolderAssetEditorDialog.java | 6 + .../file/ParticlesAssetEditorDialog.java | 38 +- 4 files changed, 43 insertions(+), 344 deletions(-) diff --git a/src/main/java/com/ss/editor/ui/dialog/asset/file/AssetEditorDialog.java b/src/main/java/com/ss/editor/ui/dialog/asset/file/AssetEditorDialog.java index 2c0df4da..c71196ea 100644 --- a/src/main/java/com/ss/editor/ui/dialog/asset/file/AssetEditorDialog.java +++ b/src/main/java/com/ss/editor/ui/dialog/asset/file/AssetEditorDialog.java @@ -1,54 +1,34 @@ package com.ss.editor.ui.dialog.asset.file; -import static com.ss.editor.Messages.ASSET_EDITOR_DIALOG_TITLE; import static com.ss.editor.ui.component.asset.tree.resource.ResourceElementFactory.createFor; import static com.ss.editor.ui.util.UIUtils.findItemForValue; import static com.ss.rlib.util.ObjectUtils.notNull; -import com.ss.editor.Editor; -import com.ss.editor.Messages; import com.ss.editor.annotation.FXThread; import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.config.EditorConfig; import com.ss.editor.manager.ExecutorManager; -import com.ss.editor.manager.JMEFilePreviewManager; -import com.ss.editor.manager.JavaFXImageManager; -import com.ss.editor.ui.Icons; import com.ss.editor.ui.component.asset.tree.ResourceTree; import com.ss.editor.ui.component.asset.tree.resource.ResourceElement; -import com.ss.editor.ui.css.CSSClasses; import com.ss.editor.ui.dialog.EditorDialog; +import com.ss.editor.ui.dialog.asset.BaseAssetEditorDialog; import com.ss.editor.ui.event.FXEventManager; import com.ss.editor.ui.event.impl.CreatedFileEvent; import com.ss.editor.ui.event.impl.DeletedFileEvent; import com.ss.editor.ui.event.impl.RequestSelectFileEvent; -import com.ss.editor.util.EditorUtil; -import com.ss.rlib.ui.util.FXUtils; -import com.ss.rlib.util.FileUtils; import com.ss.rlib.util.array.Array; import com.ss.rlib.util.array.ArrayFactory; -import javafx.beans.binding.BooleanBinding; -import javafx.beans.property.ObjectProperty; import javafx.beans.property.ReadOnlyObjectProperty; +import javafx.beans.value.ObservableBooleanValue; import javafx.event.Event; import javafx.event.EventHandler; -import javafx.scene.control.Button; -import javafx.scene.control.Label; -import javafx.scene.control.*; -import javafx.scene.control.TextArea; -import javafx.scene.image.Image; -import javafx.scene.image.ImageView; -import javafx.scene.input.KeyCode; -import javafx.scene.input.KeyEvent; +import javafx.scene.control.MultipleSelectionModel; +import javafx.scene.control.TreeItem; import javafx.scene.layout.HBox; import javafx.scene.layout.Region; -import javafx.scene.layout.StackPane; -import javafx.scene.layout.VBox; import javafx.stage.Window; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.awt.*; -import java.nio.file.Files; import java.nio.file.Path; import java.util.function.Consumer; import java.util.function.Function; @@ -60,19 +40,7 @@ * @param the type parameter * @author JavaSaBr */ -public class AssetEditorDialog extends EditorDialog { - - /** - * The dialog size. - */ - @NotNull - protected static final Point DIALOG_SIZE = new Point(-1, -1); - - /** - * The image manager. - */ - @NotNull - protected static final JavaFXImageManager JAVA_FX_IMAGE_MANAGER = JavaFXImageManager.getInstance(); +public class AssetEditorDialog extends BaseAssetEditorDialog { /** * The executing manager. @@ -86,12 +54,6 @@ public class AssetEditorDialog extends EditorDialog { @NotNull protected static final FXEventManager FX_EVENT_MANAGER = FXEventManager.getInstance(); - /** - * The editor. - */ - @NotNull - protected static final Editor EDITOR = Editor.getInstance(); - /** * The handler created files events. */ @@ -116,56 +78,19 @@ public class AssetEditorDialog extends EditorDialog { @NotNull private final Array waitedFilesToSelect; - /** - * The function for handling the choose. - */ - @NotNull - protected final Consumer consumer; - - /** - * The function for validating the choose. - */ - @Nullable - private final Function validator; - /** * The tree with all resources. */ @Nullable private ResourceTree resourceTree; - /** - * The image preview. - */ - @Nullable - protected ImageView imageView; - - /** - * The preview of text files. - */ - @Nullable - protected TextArea textView; - - /** - * The label with any warning. - */ - @Nullable - private Label warningLabel; - - /** - * The OK button. - */ - @Nullable - protected Button okButton; - public AssetEditorDialog(@NotNull final Consumer consumer) { this(consumer, null); } public AssetEditorDialog(@NotNull final Consumer consumer, @Nullable final Function validator) { + super(consumer, validator); this.waitedFilesToSelect = ArrayFactory.newArray(Path.class); - this.consumer = consumer; - this.validator = validator; } /** @@ -200,68 +125,14 @@ public void setOnlyFolders(final boolean onlyFolders) { @Override @FXThread - protected void createContent(@NotNull final VBox root) { - - final HBox container = new HBox(); + protected @NotNull Region buildFirstPart(@NotNull final HBox container) { resourceTree = new ResourceTree(this::processOpen, false); - resourceTree.prefHeightProperty().bind(root.heightProperty()); - resourceTree.prefWidthProperty().bind(root.widthProperty().multiply(0.5)); resourceTree.getSelectionModel() .selectedItemProperty() .addListener((observable, oldValue, newValue) -> processSelected(newValue)); - final Region secondPart = buildSecondPart(container); - secondPart.prefHeightProperty().bind(root.heightProperty()); - secondPart.prefWidthProperty().bind(root.widthProperty().multiply(0.5)); - - FXUtils.addToPane(resourceTree, container); - FXUtils.addToPane(secondPart, container); - FXUtils.addToPane(container, root); - - root.setOnKeyReleased(this::processKeyEvent); - - FXUtils.addClassTo(container, CSSClasses.DEF_HBOX); - FXUtils.addClassTo(root, CSSClasses.ASSET_EDITOR_DIALOG); - } - - /** - * Build second part parent. - * - * @param container the container - * @return the parent - */ - @FXThread - protected @NotNull Region buildSecondPart(@NotNull final HBox container) { - - final StackPane previewContainer = new StackPane(); - - imageView = new ImageView(); - imageView.fitHeightProperty().bind(previewContainer.heightProperty().subtract(2)); - imageView.fitWidthProperty().bind(previewContainer.widthProperty().subtract(2)); - - textView = new TextArea(); - textView.setEditable(false); - textView.prefWidthProperty().bind(previewContainer.widthProperty().subtract(2)); - textView.prefHeightProperty().bind(previewContainer.heightProperty().subtract(2)); - - FXUtils.addToPane(imageView, previewContainer); - FXUtils.addToPane(textView, previewContainer); - - FXUtils.addClassTo(previewContainer, CSSClasses.ASSET_EDITOR_DIALOG_PREVIEW_CONTAINER); - FXUtils.addClassTo(textView, CSSClasses.TRANSPARENT_TEXT_AREA); - - return previewContainer; - } - - /** - * Gets ok button. - * - * @return the ok button. - */ - @FXThread - public @NotNull Button getOkButton() { - return notNull(okButton); + return resourceTree; } /** @@ -271,15 +142,7 @@ protected void createContent(@NotNull final VBox root) { */ @FXThread protected void processOpen(@NotNull final ResourceElement element) { - hide(); - } - - @FXThread - private void processKeyEvent(@NotNull final KeyEvent event) { - final Button okButton = getOkButton(); - if (event.getCode() == KeyCode.ENTER && !okButton.isDisable()) { - processSelect(); - } + processOk(); } @Override @@ -359,105 +222,10 @@ private void processEvent(@NotNull final RequestSelectFileEvent event) { return waitedFilesToSelect; } - /** - * @return the image preview. - */ - @FXThread - private @NotNull ImageView getImageView() { - return notNull(imageView); - } - - /** - * @return the text preview. - */ - @FXThread - private @NotNull TextArea getTextView() { - return notNull(textView); - } - - /** - * Gets validator. - * - * @return the function for validating the choose. - */ - @FromAnyThread - protected @Nullable Function getValidator() { - return validator; - } - - /** - * @return the label with any warning. - */ - @FXThread - private @NotNull Label getWarningLabel() { - return notNull(warningLabel); - } - - /** - * Handle selected element in the tree. - */ - @FXThread - private void processSelected(@Nullable final TreeItem newValue) { - - final ResourceElement element = newValue == null ? null : newValue.getValue(); - final Path file = element == null ? null : element.getFile(); - - validate(getWarningLabel(), element); - - try { - updatePreview(file); - } catch (final Exception e) { - EditorUtil.handleException(LOGGER, this, e); - } - } - - /** - * Update the preview of the file. - * - * @param file the file for preview or null. - */ + @Override @FXThread - private void updatePreview(@Nullable final Path file) { - - final ImageView imageView = getImageView(); - imageView.setVisible(false); - - final TextArea textView = getTextView(); - textView.setVisible(false); - - final int width = (int) imageView.getFitWidth(); - final int height = (int) imageView.getFitHeight(); - - if (JMEFilePreviewManager.isJmeFile(file)) { - - final JMEFilePreviewManager previewManager = JMEFilePreviewManager.getInstance(); - previewManager.show(file, width, height); - - final ImageView sourceView = previewManager.getImageView(); - final ObjectProperty imageProperty = imageView.imageProperty(); - imageProperty.bind(sourceView.imageProperty()); - - imageView.setVisible(true); - - } else if (JavaFXImageManager.isImage(file)) { - - final Image preview = JAVA_FX_IMAGE_MANAGER.getImagePreview(file, width, height); - imageView.setImage(preview); - imageView.setVisible(true); - - } else if (JMEFilePreviewManager.isAudioFile(file)) { - } else if (file != null && !Files.isDirectory(file)) { - - imageView.imageProperty().unbind(); - imageView.setImage(null); - - textView.setText(FileUtils.read(file)); - textView.setVisible(true); - - } else { - imageView.imageProperty().unbind(); - imageView.setImage(null); - } + protected @Nullable Path getRealFile(@NotNull final ResourceElement element) { + return element.getFile(); } @Override @@ -468,74 +236,16 @@ public void hide() { FX_EVENT_MANAGER.removeEventHandler(RequestSelectFileEvent.EVENT_TYPE, selectFileHandle); FX_EVENT_MANAGER.removeEventHandler(DeletedFileEvent.EVENT_TYPE, deletedFileHandler); - final JMEFilePreviewManager previewManager = JMEFilePreviewManager.getInstance(); - previewManager.clear(); - super.hide(); } - /** - * Validate the resource element. - * - * @param warningLabel the warning label - * @param element the element. - */ - @FXThread - protected void validate(@NotNull final Label warningLabel, @Nullable final ResourceElement element) { - } - @Override @FXThread - protected void createActions(@NotNull final VBox root) { - - final HBox container = new HBox(); - - warningLabel = new Label(); - warningLabel.setGraphic(new ImageView(Icons.WARNING_24)); - warningLabel.setVisible(false); - - okButton = new Button(Messages.SIMPLE_DIALOG_BUTTON_SELECT); - okButton.setOnAction(event -> processSelect()); - okButton.disableProperty().bind(buildDisableCondition()); - - final Button cancelButton = new Button(Messages.SIMPLE_DIALOG_BUTTON_CANCEL); - cancelButton.setOnAction(event -> hide()); - - FXUtils.addClassTo(container, CSSClasses.DEF_HBOX); - FXUtils.addClassTo(warningLabel, CSSClasses.DIALOG_LABEL_WARNING); - FXUtils.addClassTo(okButton, cancelButton, CSSClasses.DIALOG_BUTTON); - FXUtils.addClassTo(root, CSSClasses.ASSET_EDITOR_DIALOG_ACTIONS); - - FXUtils.addToPane(warningLabel, container); - FXUtils.addToPane(okButton, container); - FXUtils.addToPane(cancelButton, container); - FXUtils.addToPane(container, root); - } - - /** - * Build disable condition boolean binding. - * - * @return the boolean binding - */ - @FXThread - protected @NotNull BooleanBinding buildDisableCondition() { - + protected @NotNull ObservableBooleanValue buildAdditionalDisableCondition() { final ResourceTree resourceTree = getResourceTree(); final MultipleSelectionModel> selectionModel = resourceTree.getSelectionModel(); final ReadOnlyObjectProperty> selectedItemProperty = selectionModel.selectedItemProperty(); - - final Label warningLabel = getWarningLabel(); - return warningLabel.visibleProperty().or(selectedItemProperty.isNull()); - } - - /** - * Gets consumer. - * - * @return the function for handling the choose. - */ - @FromAnyThread - protected @NotNull Consumer getConsumer() { - return consumer; + return selectedItemProperty.isNull(); } /** @@ -546,11 +256,10 @@ protected void createActions(@NotNull final VBox root) { return notNull(resourceTree); } - /** - * The process of choosing the element. - */ + @Override @FXThread - private void processSelect() { + protected void processOk() { + super.processOk(); final ResourceTree resourceTree = getResourceTree(); final MultipleSelectionModel> selectionModel = resourceTree.getSelectionModel(); @@ -563,16 +272,4 @@ private void processSelect() { processOpen(selectedItem.getValue()); } - - @Override - @FromAnyThread - protected @NotNull String getTitleText() { - return ASSET_EDITOR_DIALOG_TITLE; - } - - @Override - @FromAnyThread - protected @NotNull Point getSize() { - return DIALOG_SIZE; - } } diff --git a/src/main/java/com/ss/editor/ui/dialog/asset/file/FileAssetEditorDialog.java b/src/main/java/com/ss/editor/ui/dialog/asset/file/FileAssetEditorDialog.java index d38bab17..74376111 100644 --- a/src/main/java/com/ss/editor/ui/dialog/asset/file/FileAssetEditorDialog.java +++ b/src/main/java/com/ss/editor/ui/dialog/asset/file/FileAssetEditorDialog.java @@ -35,6 +35,12 @@ protected void processOpen(@NotNull final ResourceElement element) { consumer.accept(element.getFile()); } + @Override + @FXThread + protected @Nullable Path getObject(@NotNull final ResourceElement element) { + return element.getFile(); + } + @Override @FXThread protected void validate(@NotNull final Label warningLabel, @Nullable final ResourceElement element) { diff --git a/src/main/java/com/ss/editor/ui/dialog/asset/file/FolderAssetEditorDialog.java b/src/main/java/com/ss/editor/ui/dialog/asset/file/FolderAssetEditorDialog.java index a04610f2..3caf8c69 100644 --- a/src/main/java/com/ss/editor/ui/dialog/asset/file/FolderAssetEditorDialog.java +++ b/src/main/java/com/ss/editor/ui/dialog/asset/file/FolderAssetEditorDialog.java @@ -36,6 +36,12 @@ protected void processOpen(@NotNull final ResourceElement element) { consumer.accept(element.getFile()); } + @Override + @FXThread + protected @Nullable Path getObject(@NotNull final ResourceElement element) { + return element.getFile(); + } + @Override @FXThread protected void validate(@NotNull final Label warningLabel, @Nullable final ResourceElement element) { diff --git a/src/main/java/com/ss/editor/ui/dialog/asset/file/ParticlesAssetEditorDialog.java b/src/main/java/com/ss/editor/ui/dialog/asset/file/ParticlesAssetEditorDialog.java index 2e947059..2bd7716a 100644 --- a/src/main/java/com/ss/editor/ui/dialog/asset/file/ParticlesAssetEditorDialog.java +++ b/src/main/java/com/ss/editor/ui/dialog/asset/file/ParticlesAssetEditorDialog.java @@ -14,14 +14,17 @@ import com.ss.editor.ui.component.asset.tree.resource.ResourceElement; import com.ss.editor.ui.css.CSSClasses; import com.ss.rlib.ui.util.FXUtils; -import javafx.beans.binding.BooleanBinding; +import javafx.beans.binding.Bindings; +import javafx.beans.property.ReadOnlyObjectProperty; +import javafx.beans.value.ObservableBooleanValue; import javafx.collections.ObservableList; -import javafx.scene.control.*; -import javafx.scene.image.ImageView; +import javafx.scene.control.CheckBox; +import javafx.scene.control.ComboBox; +import javafx.scene.control.Label; +import javafx.scene.control.SingleSelectionModel; import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Region; -import javafx.scene.layout.StackPane; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import tonegod.emitter.material.ParticlesMaterial; @@ -69,6 +72,8 @@ public ParticlesAssetEditorDialog(@NotNull final Consumer con @Override protected @NotNull Region buildSecondPart(@NotNull final HBox container) { + final Region preview = super.buildSecondPart(container); + textureParamNameLabel = new Label(Messages.PARTICLE_ASSET_EDITOR_DIALOG_TEXTURE_PARAM_LABEL + ":"); textureParamNameLabel.prefWidthProperty().bind(container.widthProperty().multiply(0.25)); @@ -81,30 +86,14 @@ public ParticlesAssetEditorDialog(@NotNull final Consumer con applyLightingTransformCheckBox = new CheckBox(); applyLightingTransformCheckBox.prefWidthProperty().bind(container.widthProperty().multiply(0.25)); - final StackPane previewContainer = new StackPane(); - - imageView = new ImageView(); - imageView.fitHeightProperty().bind(previewContainer.heightProperty().subtract(2)); - imageView.fitWidthProperty().bind(previewContainer.widthProperty().subtract(2)); - - textView = new TextArea(); - textView.setEditable(false); - textView.prefWidthProperty().bind(previewContainer.widthProperty().subtract(2)); - textView.prefHeightProperty().bind(previewContainer.heightProperty().subtract(2)); - - FXUtils.addToPane(imageView, previewContainer); - FXUtils.addToPane(textView, previewContainer); - final GridPane settingsContainer = new GridPane(); settingsContainer.add(textureParamNameLabel, 0, 0); settingsContainer.add(textureParamNameComboBox, 1, 0); settingsContainer.add(applyLightingTransformLabel, 0, 1); settingsContainer.add(applyLightingTransformCheckBox, 1, 1); - settingsContainer.add(previewContainer, 0, 2, 2, 1); + settingsContainer.add(preview, 0, 2, 2, 1); FXUtils.addClassTo(settingsContainer, CSSClasses.DEF_GRID_PANE); - FXUtils.addClassTo(previewContainer, CSSClasses.ASSET_EDITOR_DIALOG_PREVIEW_CONTAINER); - FXUtils.addClassTo(textView, CSSClasses.TRANSPARENT_TEXT_AREA); return settingsContainer; } @@ -153,11 +142,12 @@ protected void processOpen(@NotNull final ResourceElement element) { @Override @FXThread - protected @NotNull BooleanBinding buildDisableCondition() { + protected @NotNull ObservableBooleanValue buildAdditionalDisableCondition() { final ComboBox comboBox = getTextureParamNameComboBox(); final SingleSelectionModel selectionModel = comboBox.getSelectionModel(); - return super.buildDisableCondition().or(selectionModel.selectedItemProperty() - .isNull().or(selectionModel.selectedItemProperty().isEqualTo(""))); + final ReadOnlyObjectProperty itemProperty = selectionModel.selectedItemProperty(); + final ObservableBooleanValue parent = super.buildAdditionalDisableCondition(); + return Bindings.and(parent, itemProperty.isNull().or(itemProperty.isEqualTo(""))); } @Override From 4fc9e84ee9eafd236b7b21d590ab7ca6e4e8ef82 Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Wed, 13 Sep 2017 19:51:34 +0300 Subject: [PATCH 14/50] refactoring --- .../Advanced3DFileEditorWithRightTool.java | 4 ++ .../plugin/api/editor/BaseFileEditor.java | 1 + .../material/BaseMaterialFileEditor.java | 1 + .../menu/action/OpenFileInExplorerAction.java | 13 +----- .../TerrainEditingStateWithEditorTool.java | 25 +++++++++++ .../editor/impl/AudioViewerEditor.java | 45 ++++++++++--------- .../editor/impl/CodeAreaFileEditor.java | 10 +++++ .../component/editor/impl/GLSLFileEditor.java | 4 ++ .../editor/impl/ImageViewerEditor.java | 21 +++++---- .../impl/MaterialDefinitionFileEditor.java | 10 +++-- .../component/editor/impl/TextFileEditor.java | 34 +++++++++----- .../impl/material/MaterialFileEditor.java | 3 ++ .../editor/impl/model/ModelFileEditor.java | 20 ++++++++- .../impl/scene/AbstractSceneFileEditor.java | 3 ++ .../editor/impl/scene/SceneFileEditor.java | 35 +++++++++++++++ .../component/editor/state/EditorState.java | 3 ++ .../editor/state/EditorToolConfig.java | 6 +++ .../state/impl/AbstractEditorState.java | 18 ++++---- .../impl/BaseEditorSceneEditorState.java | 14 ++++-- .../state/impl/Editor3DEditorState.java | 17 ++++--- .../Editor3DWithEditorToolEditorState.java | 5 +++ .../state/impl/EditorMaterialEditorState.java | 13 ++++-- .../state/impl/EditorModelEditorState.java | 9 ++++ .../state/impl/EditorSceneEditorState.java | 8 ++-- .../editor/state/impl/VoidEditorState.java | 3 ++ .../split/pane/EditorToolSplitPane.java | 18 +++++--- 26 files changed, 256 insertions(+), 87 deletions(-) diff --git a/src/main/java/com/ss/editor/plugin/api/editor/Advanced3DFileEditorWithRightTool.java b/src/main/java/com/ss/editor/plugin/api/editor/Advanced3DFileEditorWithRightTool.java index c8d66d79..908ae1f6 100644 --- a/src/main/java/com/ss/editor/plugin/api/editor/Advanced3DFileEditorWithRightTool.java +++ b/src/main/java/com/ss/editor/plugin/api/editor/Advanced3DFileEditorWithRightTool.java @@ -75,6 +75,7 @@ protected void createContent(@NotNull final StackPane root) { /** * Create editor area pane. */ + @FXThread protected void createEditorAreaPane() { editorAreaPane = new StackPane(); @@ -134,6 +135,7 @@ protected void loadState() { * @param container the tool container. * @param root the root. */ + @FXThread protected void createToolComponents(@NotNull final EditorToolComponent container, @NotNull final StackPane root) { } @@ -142,6 +144,7 @@ protected void createToolComponents(@NotNull final EditorToolComponent container * * @param dragEvent the drag event. */ + @FXThread protected void handleDragOverEvent(@NotNull final DragEvent dragEvent) { } @@ -150,6 +153,7 @@ protected void handleDragOverEvent(@NotNull final DragEvent dragEvent) { * * @param dragEvent the drop event. */ + @FXThread protected void handleDragDroppedEvent(@NotNull final DragEvent dragEvent) { } } diff --git a/src/main/java/com/ss/editor/plugin/api/editor/BaseFileEditor.java b/src/main/java/com/ss/editor/plugin/api/editor/BaseFileEditor.java index 7ba517c4..cca15f6b 100644 --- a/src/main/java/com/ss/editor/plugin/api/editor/BaseFileEditor.java +++ b/src/main/java/com/ss/editor/plugin/api/editor/BaseFileEditor.java @@ -70,6 +70,7 @@ protected void loadState() { * * @return the factory to make an editor state. */ + @FXThread protected @Nullable Supplier getEditorStateFactory() { return null; } diff --git a/src/main/java/com/ss/editor/plugin/api/editor/material/BaseMaterialFileEditor.java b/src/main/java/com/ss/editor/plugin/api/editor/material/BaseMaterialFileEditor.java index 021a2dc5..7e17886d 100644 --- a/src/main/java/com/ss/editor/plugin/api/editor/material/BaseMaterialFileEditor.java +++ b/src/main/java/com/ss/editor/plugin/api/editor/material/BaseMaterialFileEditor.java @@ -222,6 +222,7 @@ protected void loadState() { getLightButton().setSelected(editorState.isLightEnable()); } + @FXThread @Override protected @Nullable Supplier getEditorStateFactory() { return EditorMaterialEditorState::new; diff --git a/src/main/java/com/ss/editor/ui/component/asset/tree/context/menu/action/OpenFileInExplorerAction.java b/src/main/java/com/ss/editor/ui/component/asset/tree/context/menu/action/OpenFileInExplorerAction.java index 8fcf29a6..d5b13763 100644 --- a/src/main/java/com/ss/editor/ui/component/asset/tree/context/menu/action/OpenFileInExplorerAction.java +++ b/src/main/java/com/ss/editor/ui/component/asset/tree/context/menu/action/OpenFileInExplorerAction.java @@ -1,12 +1,9 @@ package com.ss.editor.ui.component.asset.tree.context.menu.action; -import com.ss.editor.FileExtensions; import com.ss.editor.Messages; -import com.ss.editor.manager.ResourceManager; import com.ss.editor.ui.Icons; import com.ss.editor.ui.component.asset.tree.resource.ResourceElement; -import com.ss.editor.ui.util.UIUtils; -import com.ss.rlib.util.array.Array; +import com.ss.editor.util.EditorUtil; import javafx.event.ActionEvent; import javafx.scene.image.Image; import org.jetbrains.annotations.NotNull; @@ -36,12 +33,6 @@ public OpenFileInExplorerAction(@NotNull final ResourceElement element) { @Override protected void execute(@Nullable final ActionEvent event) { super.execute(event); - - final ResourceManager resourceManager = ResourceManager.getInstance(); - final Array shaderNodes = resourceManager.getAvailableResources(FileExtensions.JME_SHADER_NODE); - - UIUtils.openResourceAssetDialog(s -> System.out.println(s), shaderNodes); - - //EditorUtil.openFileInSystemExplorer(getElement().getFile()); + EditorUtil.openFileInSystemExplorer(getElement().getFile()); } } diff --git a/src/main/java/com/ss/editor/ui/component/editing/terrain/TerrainEditingStateWithEditorTool.java b/src/main/java/com/ss/editor/ui/component/editing/terrain/TerrainEditingStateWithEditorTool.java index 6d77c254..f0be389e 100644 --- a/src/main/java/com/ss/editor/ui/component/editing/terrain/TerrainEditingStateWithEditorTool.java +++ b/src/main/java/com/ss/editor/ui/component/editing/terrain/TerrainEditingStateWithEditorTool.java @@ -1,6 +1,7 @@ package com.ss.editor.ui.component.editing.terrain; import static java.lang.Math.abs; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.component.editor.state.impl.AbstractEditorState; import com.ss.editor.ui.component.editor.state.impl.AdditionalEditorState; @@ -45,120 +46,144 @@ public TerrainEditingStateWithEditorTool() { this.slopeSmoothly = true; } + @FXThread public float getBrushSize() { return brushSize; } + @FXThread public void setBrushSize(final float brushSize) { final boolean changed = abs(getBrushSize() - brushSize) > 0.001f; this.brushSize = brushSize; if (changed) notifyChange(); } + @FXThread public float getBrushPower() { return brushPower; } + @FXThread public void setBrushPower(final float brushPower) { final boolean changed = abs(getBrushPower() - brushPower) > 0.001f; this.brushPower = brushPower; if (changed) notifyChange(); } + @FXThread public float getLevelValue() { return levelValue; } + @FXThread public void setLevelValue(final float levelValue) { final boolean changed = abs(getLevelValue() - levelValue) > 0.001f; this.levelValue = levelValue; if (changed) notifyChange(); } + @FXThread public float getRoughtFrequency() { return roughtFrequency; } + @FXThread public void setRoughtFrequency(final float roughtFrequency) { final boolean changed = abs(getRoughtFrequency() - roughtFrequency) > 0.001f; this.roughtFrequency = roughtFrequency; if (changed) notifyChange(); } + @FXThread public float getRoughtLacunarity() { return roughtLacunarity; } + @FXThread public void setRoughtLacunarity(final float roughtLacunarity) { final boolean changed = abs(getRoughtLacunarity() - roughtLacunarity) > 0.001f; this.roughtLacunarity = roughtLacunarity; if (changed) notifyChange(); } + @FXThread public float getRoughtOctaves() { return roughtOctaves; } + @FXThread public void setRoughtOctaves(final float roughtOctaves) { final boolean changed = abs(getRoughtOctaves() - roughtOctaves) > 0.001f; this.roughtOctaves = roughtOctaves; if (changed) notifyChange(); } + @FXThread public float getRoughtRoughness() { return roughtRoughness; } + @FXThread public void setRoughtRoughness(final float roughtRoughness) { final boolean changed = abs(getRoughtRoughness() - roughtRoughness) > 0.001f; this.roughtRoughness = roughtRoughness; if (changed) notifyChange(); } + @FXThread public float getRoughtScale() { return roughtScale; } + @FXThread public void setRoughtScale(final float roughtScale) { final boolean changed = abs(getRoughtScale() - roughtScale) > 0.001f; this.roughtScale = roughtScale; if (changed) notifyChange(); } + @FXThread public boolean isLevelUseMarker() { return levelUseMarker; } + @FXThread public void setLevelUseMarker(final boolean levelUseMarker) { final boolean changed = isLevelUseMarker() != levelUseMarker; this.levelUseMarker = levelUseMarker; if (changed) notifyChange(); } + @FXThread public boolean isLevelSmoothly() { return levelSmoothly; } + @FXThread public void setLevelSmoothly(final boolean levelSmoothly) { final boolean changed = isLevelSmoothly() != levelSmoothly; this.levelSmoothly = levelSmoothly; if (changed) notifyChange(); } + @FXThread public boolean isSlopeLimited() { return slopeLimited; } + @FXThread public void setSlopeLimited(final boolean slopeLimited) { final boolean changed = isSlopeLimited() != slopeLimited; this.slopeLimited = slopeLimited; if (changed) notifyChange(); } + @FXThread public boolean isSlopeSmoothly() { return slopeSmoothly; } + @FXThread public void setSlopeSmoothly(final boolean slopeSmoothly) { final boolean changed = isSlopeSmoothly() != slopeSmoothly; this.slopeSmoothly = slopeSmoothly; diff --git a/src/main/java/com/ss/editor/ui/component/editor/impl/AudioViewerEditor.java b/src/main/java/com/ss/editor/ui/component/editor/impl/AudioViewerEditor.java index 9b9e2c0a..dcf98ff3 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/impl/AudioViewerEditor.java +++ b/src/main/java/com/ss/editor/ui/component/editor/impl/AudioViewerEditor.java @@ -103,14 +103,14 @@ private AudioViewerEditor() { addEditorState(editorAppState); } - @NotNull @Override - protected VBox createRoot() { + @FXThread + protected @NotNull VBox createRoot() { return new VBox(); } - @Override + @FXThread protected void createContent(@NotNull final VBox root) { final Label durationLabel = new Label(Messages.AUDIO_VIEWER_EDITOR_DURATION_LABEL + ":"); @@ -174,6 +174,7 @@ protected void createContent(@NotNull final VBox root) { /** * Stop of plying. */ + @FXThread private void processStop() { getEditorAppState().stop(); } @@ -181,6 +182,7 @@ private void processStop() { /** * Play the audio. */ + @FXThread private void processPlay() { final AudioViewer3DState appState = getEditorAppState(); if (appState.getPrevStatus() == Playing) { @@ -190,8 +192,8 @@ private void processPlay() { } } - @FXThread @Override + @FXThread public void openFile(@NotNull final Path file) { super.openFile(file); @@ -218,18 +220,17 @@ public void openFile(@NotNull final Path file) { getBitsPerSampleField().setText(String.valueOf(bitsPerSample)); } - @NotNull @Override - public EditorDescription getDescription() { + @FromAnyThread + public @NotNull EditorDescription getDescription() { return DESCRIPTION; } /** * @return the editor app state. */ - @NotNull @FromAnyThread - private AudioViewer3DState getEditorAppState() { + private @NotNull AudioViewer3DState getEditorAppState() { return editorAppState; } @@ -268,56 +269,56 @@ public void notifyChangedStatus(final AudioSource.Status status) { /** * @return the play button. */ - @NotNull - private Button getPlayButton() { + @FXThread + private @NotNull Button getPlayButton() { return notNull(playButton); } /** * @return the stop button. */ - @NotNull - private Button getStopButton() { + @FXThread + private @NotNull Button getStopButton() { return notNull(stopButton); } /** * @return the channels field. */ - @NotNull - private TextField getChannelsField() { + @FXThread + private @NotNull TextField getChannelsField() { return notNull(channelsField); } /** * @return the duration field. */ - @NotNull - private TextField getDurationField() { + @FXThread + private @NotNull TextField getDurationField() { return notNull(durationField); } /** * @return the data type field. */ - @NotNull - private TextField getDataTypeField() { + @FXThread + private @NotNull TextField getDataTypeField() { return notNull(dataTypeField); } /** * @return the sample rate field. */ - @NotNull - private TextField getSampleRateField() { + @FXThread + private @NotNull TextField getSampleRateField() { return notNull(sampleRateField); } /** * @return the bits per sample field. */ - @NotNull - private TextField getBitsPerSampleField() { + @FXThread + private @NotNull TextField getBitsPerSampleField() { return notNull(bitsPerSampleField); } diff --git a/src/main/java/com/ss/editor/ui/component/editor/impl/CodeAreaFileEditor.java b/src/main/java/com/ss/editor/ui/component/editor/impl/CodeAreaFileEditor.java index 6eb713ed..58a40cf9 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/impl/CodeAreaFileEditor.java +++ b/src/main/java/com/ss/editor/ui/component/editor/impl/CodeAreaFileEditor.java @@ -40,11 +40,13 @@ public abstract class CodeAreaFileEditor extends AbstractFileEditor { private CodeArea codeArea; @Override + @FXThread protected @NotNull VBox createRoot() { return new VBox(); } @Override + @FXThread protected void createContent(@NotNull final VBox root) { codeArea = new CodeArea(); @@ -64,6 +66,7 @@ protected void createContent(@NotNull final VBox root) { * @param text the text * @return the style spans */ + @FXThread protected @NotNull StyleSpans> getStyleSpans(@NotNull final String text) { throw new RuntimeException("unsupported"); } @@ -71,16 +74,19 @@ protected void createContent(@NotNull final VBox root) { /** * Update dirty state. */ + @FXThread private void updateDirty(final String newContent) { setDirty(!getOriginalContent().equals(newContent)); } @Override + @FXThread protected boolean needToolbar() { return true; } @Override + @FXThread protected void createToolbar(@NotNull final HBox container) { super.createToolbar(container); FXUtils.addToPane(createSaveAction(), container); @@ -89,6 +95,7 @@ protected void createToolbar(@NotNull final HBox container) { /** * @return the code area. */ + @FXThread private @NotNull CodeArea getCodeArea() { return notNull(codeArea); } @@ -113,6 +120,7 @@ public void openFile(@NotNull final Path file) { /** * @return the original content of the opened file. */ + @FXThread private @NotNull String getOriginalContent() { return notNull(originalContent); } @@ -120,6 +128,7 @@ public void openFile(@NotNull final Path file) { /** * @param originalContent the original content of the opened file. */ + @FXThread private void setOriginalContent(@NotNull final String originalContent) { this.originalContent = originalContent; } @@ -150,6 +159,7 @@ protected void postSave() { } @Override + @FXThread protected void handleExternalChanges() { super.handleExternalChanges(); diff --git a/src/main/java/com/ss/editor/ui/component/editor/impl/GLSLFileEditor.java b/src/main/java/com/ss/editor/ui/component/editor/impl/GLSLFileEditor.java index d4c6e4bf..5c9e788d 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/impl/GLSLFileEditor.java +++ b/src/main/java/com/ss/editor/ui/component/editor/impl/GLSLFileEditor.java @@ -3,6 +3,8 @@ import static java.util.Collections.singleton; import com.ss.editor.FileExtensions; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.component.editor.EditorDescription; import org.fxmisc.richtext.model.StyleSpans; import org.fxmisc.richtext.model.StyleSpansBuilder; @@ -123,12 +125,14 @@ private static StyleSpans> computeHighlighting(@NotNull final return spansBuilder.create(); } + @FXThread @Override protected @NotNull StyleSpans> getStyleSpans(@NotNull final String text) { return computeHighlighting(text); } @Override + @FromAnyThread public @NotNull EditorDescription getDescription() { return DESCRIPTION; } diff --git a/src/main/java/com/ss/editor/ui/component/editor/impl/ImageViewerEditor.java b/src/main/java/com/ss/editor/ui/component/editor/impl/ImageViewerEditor.java index 6bc36e0c..ddba00ac 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/impl/ImageViewerEditor.java +++ b/src/main/java/com/ss/editor/ui/component/editor/impl/ImageViewerEditor.java @@ -5,11 +5,11 @@ import com.ss.editor.FileExtensions; import com.ss.editor.Messages; import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.manager.JavaFXImageManager; import com.ss.editor.ui.component.editor.EditorDescription; import com.ss.editor.ui.css.CSSClasses; import com.ss.editor.ui.event.impl.FileChangedEvent; -import com.ss.rlib.plugin.annotation.PluginDescription; import com.ss.rlib.ui.util.FXUtils; import javafx.scene.image.Image; import javafx.scene.image.ImageView; @@ -32,7 +32,7 @@ public class ImageViewerEditor extends AbstractFileEditor { @NotNull public static final EditorDescription DESCRIPTION = new EditorDescription(); - @PluginDescription + @NotNull private static final JavaFXImageManager JAVA_FX_IMAGE_MANAGER = JavaFXImageManager.getInstance(); private static final int IMAGE_SIZE = 512; @@ -50,13 +50,14 @@ public class ImageViewerEditor extends AbstractFileEditor { @Nullable private ImageView imageView; - @NotNull @Override - protected VBox createRoot() { + @FXThread + protected @NotNull VBox createRoot() { return new VBox(); } @Override + @FXThread protected void createContent(@NotNull final VBox root) { imageView = new ImageView(); @@ -68,34 +69,36 @@ protected void createContent(@NotNull final VBox root) { /** * @return the image view. */ - @NotNull - private ImageView getImageView() { + @FXThread + private @NotNull ImageView getImageView() { return notNull(imageView); } @Override + @FXThread protected void processChangedFile(@NotNull final FileChangedEvent event) { final Path file = event.getFile(); if (!getEditFile().equals(file)) return; EXECUTOR_MANAGER.schedule(() -> EXECUTOR_MANAGER.addFXTask(() -> showImage(file)), 1000); } + @FXThread private void showImage(@NotNull final Path file) { final Image preview = JAVA_FX_IMAGE_MANAGER.getImagePreview(file, IMAGE_SIZE, IMAGE_SIZE); final ImageView imageView = getImageView(); imageView.setImage(preview); } - @FXThread @Override + @FXThread public void openFile(@NotNull final Path file) { super.openFile(file); showImage(file); } - @NotNull @Override - public EditorDescription getDescription() { + @FromAnyThread + public @NotNull EditorDescription getDescription() { return DESCRIPTION; } } diff --git a/src/main/java/com/ss/editor/ui/component/editor/impl/MaterialDefinitionFileEditor.java b/src/main/java/com/ss/editor/ui/component/editor/impl/MaterialDefinitionFileEditor.java index 15d48ce4..034eb1de 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/impl/MaterialDefinitionFileEditor.java +++ b/src/main/java/com/ss/editor/ui/component/editor/impl/MaterialDefinitionFileEditor.java @@ -3,6 +3,8 @@ import static java.util.Collections.singleton; import com.ss.editor.FileExtensions; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.component.editor.EditorDescription; import org.fxmisc.richtext.model.StyleSpans; import org.fxmisc.richtext.model.StyleSpansBuilder; @@ -134,15 +136,15 @@ private static StyleSpans> computeHighlighting(final String t return spansBuilder.create(); } - @NotNull @Override - protected StyleSpans> getStyleSpans(@NotNull final String text) { + @FXThread + protected @NotNull StyleSpans> getStyleSpans(@NotNull final String text) { return computeHighlighting(text); } - @NotNull @Override - public EditorDescription getDescription() { + @FromAnyThread + public @NotNull EditorDescription getDescription() { return DESCRIPTION; } } diff --git a/src/main/java/com/ss/editor/ui/component/editor/impl/TextFileEditor.java b/src/main/java/com/ss/editor/ui/component/editor/impl/TextFileEditor.java index e8632baf..d5903ac8 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/impl/TextFileEditor.java +++ b/src/main/java/com/ss/editor/ui/component/editor/impl/TextFileEditor.java @@ -4,6 +4,7 @@ import com.ss.editor.Messages; import com.ss.editor.annotation.BackgroundThread; import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.component.editor.EditorDescription; import com.ss.editor.ui.component.editor.EditorRegistry; import com.ss.editor.ui.css.CSSClasses; @@ -52,13 +53,14 @@ public class TextFileEditor extends AbstractFileEditor { @Nullable private TextArea textArea; - @NotNull @Override - protected VBox createRoot() { + @FXThread + protected @NotNull VBox createRoot() { return new VBox(); } @Override + @FXThread protected void createContent(@NotNull final VBox root) { textArea = new TextArea(); @@ -73,16 +75,19 @@ protected void createContent(@NotNull final VBox root) { /** * Update dirty state. */ + @FXThread private void updateDirty(final String newContent) { setDirty(!getOriginalContent().equals(newContent)); } @Override + @FXThread protected boolean needToolbar() { return true; } @Override + @FXThread protected void createToolbar(@NotNull final HBox container) { super.createToolbar(container); FXUtils.addToPane(createSaveAction(), container); @@ -91,18 +96,25 @@ protected void createToolbar(@NotNull final HBox container) { /** * @return the text area. */ - @NotNull - private TextArea getTextArea() { + @FXThread + private @NotNull TextArea getTextArea() { return notNull(textArea); } - @FXThread @Override + @FXThread public void openFile(@NotNull final Path file) { super.openFile(file); setOriginalContent(FileUtils.read(file)); + /* TODO added to handle some exceptions + try { + + } catch (final MalformedInputException e) { + throw new RuntimeException("This file isn't a text file.", e); + } */ + final TextArea textArea = getTextArea(); textArea.setText(getOriginalContent()); } @@ -110,14 +122,15 @@ public void openFile(@NotNull final Path file) { /** * @return the original content of the opened file. */ - @NotNull - private String getOriginalContent() { + @FXThread + private @NotNull String getOriginalContent() { return notNull(originalContent); } /** * @param originalContent the original content of the opened file. */ + @FXThread private void setOriginalContent(@NotNull final String originalContent) { this.originalContent = originalContent; } @@ -135,8 +148,8 @@ public void doSave(@NotNull final Path toStore) throws IOException { } } - @FXThread @Override + @FXThread protected void postSave() { super.postSave(); @@ -148,6 +161,7 @@ protected void postSave() { } @Override + @FXThread protected void handleExternalChanges() { super.handleExternalChanges(); @@ -161,9 +175,9 @@ protected void handleExternalChanges() { updateDirty(newContent); } - @NotNull @Override - public EditorDescription getDescription() { + @FromAnyThread + public @NotNull EditorDescription getDescription() { return DESCRIPTION; } } diff --git a/src/main/java/com/ss/editor/ui/component/editor/impl/material/MaterialFileEditor.java b/src/main/java/com/ss/editor/ui/component/editor/impl/material/MaterialFileEditor.java index 8a56ad4d..fad33d0f 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/impl/material/MaterialFileEditor.java +++ b/src/main/java/com/ss/editor/ui/component/editor/impl/material/MaterialFileEditor.java @@ -203,6 +203,7 @@ private void applyTexture(@NotNull final MaterialFileEditor editor, @NotNull fin } @Override + @FXThread protected void handleDragDroppedEvent(@NotNull final DragEvent dragEvent) { super.handleDragDroppedEvent(dragEvent); UIUtils.handleDroppedFile(dragEvent, FileExtensions.TEXTURE_EXTENSIONS, this, @@ -210,6 +211,7 @@ protected void handleDragDroppedEvent(@NotNull final DragEvent dragEvent) { } @Override + @FXThread protected void handleDragOverEvent(@NotNull final DragEvent dragEvent) { super.handleDragOverEvent(dragEvent); UIUtils.acceptIfHasFile(dragEvent, FileExtensions.TEXTURE_EXTENSIONS); @@ -233,6 +235,7 @@ protected void doOpenFile(@NotNull final Path file) throws IOException { reload(material); } + @FXThread @Override protected @Nullable Supplier getEditorStateFactory() { return EditorMaterialEditorState::new; diff --git a/src/main/java/com/ss/editor/ui/component/editor/impl/model/ModelFileEditor.java b/src/main/java/com/ss/editor/ui/component/editor/impl/model/ModelFileEditor.java index e6a217e7..434c19c0 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/impl/model/ModelFileEditor.java +++ b/src/main/java/com/ss/editor/ui/component/editor/impl/model/ModelFileEditor.java @@ -15,6 +15,7 @@ import com.ss.editor.FileExtensions; import com.ss.editor.Messages; import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.manager.ResourceManager; import com.ss.editor.state.editor.impl.model.ModelEditor3DState; import com.ss.editor.state.editor.impl.model.ModelEditorBulletState; @@ -124,6 +125,7 @@ private ModelFileEditor() { /** * @return the list of fast skies. */ + @FXThread private @NotNull ComboBox getFastSkyComboBox() { return notNull(fastSkyComboBox); } @@ -131,6 +133,7 @@ private ModelFileEditor() { /** * @return the light toggle. */ + @FXThread private @NotNull ToggleButton getLightButton() { return notNull(lightButton); } @@ -138,6 +141,7 @@ private ModelFileEditor() { /** * @return the physics toggle. */ + @FXThread private @NotNull ToggleButton getPhysicsButton() { return notNull(physicsButton); } @@ -145,6 +149,7 @@ private ModelFileEditor() { /** * @return the debug physics button. */ + @FXThread private @NotNull ToggleButton getDebugPhysicsButton() { return notNull(debugPhysicsButton); } @@ -182,6 +187,7 @@ protected void doOpenFile(@NotNull final Path file) throws IOException { } @Override + @FXThread protected void loadState() { super.loadState(); @@ -201,11 +207,13 @@ protected void loadState() { } @Override + @FXThread protected @Nullable Supplier getEditorStateFactory() { return EditorModelEditorState::new; } @Override + @FXThread protected void handleAddedObject(@NotNull final Spatial model) { super.handleAddedObject(model); @@ -223,8 +231,8 @@ protected void handleAddedObject(@NotNull final Spatial model) { } } - @FXThread @Override + @FXThread protected void handleRemovedObject(@NotNull final Spatial model) { super.handleRemovedObject(model); @@ -243,11 +251,13 @@ protected void handleRemovedObject(@NotNull final Spatial model) { } @Override + @FromAnyThread public @NotNull EditorDescription getDescription() { return DESCRIPTION; } @Override + @FXThread protected void createToolbar(@NotNull final HBox container) { super.createToolbar(container); @@ -270,6 +280,7 @@ protected void createToolbar(@NotNull final HBox container) { } @Override + @FXThread protected void createActions(@NotNull final HBox container) { super.createActions(container); @@ -303,6 +314,7 @@ protected void createActions(@NotNull final HBox container) { /** * Handle changing a sky. */ + @FXThread private void changeFastSky(@NotNull final String newSky) { if (isIgnoreListeners()) return; @@ -336,6 +348,7 @@ private void changeFastSky(@NotNull final String newSky) { /** * @return the bullet state. */ + @FXThread private @NotNull ModelEditorBulletState getBulletState() { return bulletState; } @@ -343,6 +356,7 @@ private void changeFastSky(@NotNull final String newSky) { /** * Handle to change enabling of physics. */ + @FXThread private void changePhysics(@NotNull final Boolean newValue) { if (isIgnoreListeners()) return; @@ -354,6 +368,7 @@ private void changePhysics(@NotNull final Boolean newValue) { /** * Handle to change enabling of physics. */ + @FXThread private void changeDebugPhysics(@NotNull final Boolean newValue) { if (isIgnoreListeners()) return; @@ -365,6 +380,7 @@ private void changeDebugPhysics(@NotNull final Boolean newValue) { /** * Handle changing camera light visibility. */ + @FXThread private void changeLight(@NotNull final Boolean newValue) { if (isIgnoreListeners()) return; @@ -375,6 +391,7 @@ private void changeLight(@NotNull final Boolean newValue) { } @Override + @FXThread public void notifyFXAddedChild(@NotNull final Object parent, @NotNull final Object added, final int index, final boolean needSelect) { super.notifyFXAddedChild(parent, added, index, needSelect); @@ -396,6 +413,7 @@ public void notifyFXAddedChild(@NotNull final Object parent, @NotNull final Obje } @Override + @FXThread public void notifyFXRemovedChild(@NotNull final Object parent, @NotNull final Object removed) { super.notifyFXRemovedChild(parent, removed); diff --git a/src/main/java/com/ss/editor/ui/component/editor/impl/scene/AbstractSceneFileEditor.java b/src/main/java/com/ss/editor/ui/component/editor/impl/scene/AbstractSceneFileEditor.java index 20226bb1..39212e0b 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/impl/scene/AbstractSceneFileEditor.java +++ b/src/main/java/com/ss/editor/ui/component/editor/impl/scene/AbstractSceneFileEditor.java @@ -972,6 +972,7 @@ protected void createContent(@NotNull final StackPane root) { } @Override + @FXThread protected void createToolComponents(@NotNull final EditorToolComponent container, @NotNull final StackPane root) { super.createToolComponents(container, root); @@ -1041,6 +1042,7 @@ protected void processChangeTool(@Nullable final Number oldValue, @NotNull final } @Override + @FXThread protected void handleDragDroppedEvent(@NotNull final DragEvent dragEvent) { super.handleDragDroppedEvent(dragEvent); @@ -1052,6 +1054,7 @@ protected void handleDragDroppedEvent(@NotNull final DragEvent dragEvent) { } @Override + @FXThread protected void handleDragOverEvent(@NotNull final DragEvent dragEvent) { super.handleDragOverEvent(dragEvent); UIUtils.acceptIfHasFile(dragEvent, ACCEPTED_FILES); diff --git a/src/main/java/com/ss/editor/ui/component/editor/impl/scene/SceneFileEditor.java b/src/main/java/com/ss/editor/ui/component/editor/impl/scene/SceneFileEditor.java index 69f4dd0c..1f306b1a 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/impl/scene/SceneFileEditor.java +++ b/src/main/java/com/ss/editor/ui/component/editor/impl/scene/SceneFileEditor.java @@ -9,6 +9,7 @@ import com.ss.editor.FileExtensions; import com.ss.editor.Messages; import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.extension.property.EditableProperty; import com.ss.editor.extension.scene.SceneLayer; import com.ss.editor.extension.scene.SceneNode; @@ -138,6 +139,7 @@ private SceneFileEditor() { } @Override + @FXThread protected @Nullable Supplier getEditorStateFactory() { return EditorSceneEditorState::new; } @@ -174,6 +176,7 @@ protected void doOpenFile(@NotNull final Path file) throws IOException { } @Override + @FXThread protected void refreshTree() { super.refreshTree(); @@ -189,6 +192,7 @@ protected void refreshTree() { layerNodeTree.fill(new LayersRoot(this)); } + @FXThread private void updateVisibility(@NotNull final Spatial spatial) { final SceneLayer layer = SceneLayer.getLayer(spatial); if (layer != null) spatial.setVisible(layer.isShowed()); @@ -197,6 +201,7 @@ private void updateVisibility(@NotNull final Spatial spatial) { /** * @return the list with app states. */ + @FXThread private @NotNull AppStateList getAppStateList() { return notNull(appStateList); } @@ -204,6 +209,7 @@ private void updateVisibility(@NotNull final Spatial spatial) { /** * @return the list with filters. */ + @FXThread private @NotNull FilterList getFilterList() { return notNull(filterList); } @@ -211,6 +217,7 @@ private void updateVisibility(@NotNull final Spatial spatial) { /** * @return the tree with layers. */ + @FXThread private @NotNull LayerNodeTree getLayerNodeTree() { return notNull(layerNodeTree); } @@ -218,6 +225,7 @@ private void updateVisibility(@NotNull final Spatial spatial) { /** * @return the container of property editor in layers tool. */ + @FXThread private @NotNull VBox getPropertyEditorLayersContainer() { return notNull(propertyEditorLayersContainer); } @@ -225,6 +233,7 @@ private void updateVisibility(@NotNull final Spatial spatial) { /** * @return the container of property editor in filters tool. */ + @FXThread private @NotNull VBox getPropertyEditorFiltersContainer() { return notNull(propertyEditorFiltersContainer); } @@ -232,11 +241,13 @@ private void updateVisibility(@NotNull final Spatial spatial) { /** * @return the container of property editor in app states tool. */ + @FXThread private @NotNull VBox getPropertyEditorAppStateContainer() { return notNull(propertyEditorAppStateContainer); } @Override + @FXThread protected void createToolbar(@NotNull final HBox container) { super.createToolbar(container); @@ -263,6 +274,7 @@ protected void createToolbar(@NotNull final HBox container) { /** * Handle changing light models visibility. */ + @FXThread private void changeLight(@NotNull final Boolean newValue) { if (isIgnoreListeners()) return; @@ -275,6 +287,7 @@ private void changeLight(@NotNull final Boolean newValue) { /** * Handle changing audio models visibility. */ + @FXThread private void changeAudio(@NotNull final Boolean newValue) { if (isIgnoreListeners()) return; @@ -285,6 +298,7 @@ private void changeAudio(@NotNull final Boolean newValue) { } @Override + @FXThread protected void createContent(@NotNull final StackPane root) { appStateList = new AppStateList(this::selectAppStateFromList, this); @@ -302,6 +316,7 @@ protected void createContent(@NotNull final StackPane root) { } @Override + @FXThread protected void createToolComponents(@NotNull final EditorToolComponent container, @NotNull final StackPane root) { super.createToolComponents(container, root); @@ -314,6 +329,7 @@ protected void createToolComponents(@NotNull final EditorToolComponent container } @Override + @FXThread protected void processChangeTool(@Nullable final Number oldValue, @NotNull final Number newValue) { super.processChangeTool(oldValue, newValue); @@ -347,6 +363,7 @@ protected void processChangeTool(@Nullable final Number oldValue, @NotNull final /** * @return the light toggle. */ + @FXThread private @NotNull ToggleButton getLightButton() { return notNull(lightButton); } @@ -354,11 +371,13 @@ protected void processChangeTool(@Nullable final Number oldValue, @NotNull final /** * @return the audio toggle. */ + @FXThread private @NotNull ToggleButton getAudioButton() { return notNull(audioButton); } @Override + @FXThread protected void loadState() { super.loadState(); @@ -446,6 +465,7 @@ private void selectNodeFromLayersTree(@Nullable final Object object) { } @Override + @FXThread public void selectNodeFromTree(@Nullable final Object object) { if (!isNeedSyncSelection()) return; @@ -469,6 +489,7 @@ public void selectNodeFromTree(@Nullable final Object object) { } @Override + @FromAnyThread public @NotNull EditorDescription getDescription() { return DESCRIPTION; } @@ -476,6 +497,7 @@ public void selectNodeFromTree(@Nullable final Object object) { /** * @param needSyncSelection true if need sync selection. */ + @FXThread private void setNeedSyncSelection(final boolean needSyncSelection) { this.needSyncSelection = needSyncSelection; } @@ -483,11 +505,13 @@ private void setNeedSyncSelection(final boolean needSyncSelection) { /** * @return true if need sync selection. */ + @FXThread private boolean isNeedSyncSelection() { return needSyncSelection; } @Override + @FXThread protected void updateSelection(@Nullable Spatial spatial) { if (spatial instanceof SceneNode || spatial instanceof SceneLayer) { @@ -498,6 +522,7 @@ protected void updateSelection(@Nullable Spatial spatial) { } @Override + @FXThread protected void handleAddedObject(@NotNull final Spatial model) { super.handleAddedObject(model); @@ -517,6 +542,7 @@ protected void handleAddedObject(@NotNull final Spatial model) { } @Override + @FXThread protected void handleRemovedObject(@NotNull final Spatial model) { super.handleRemovedObject(model); @@ -536,6 +562,7 @@ protected void handleRemovedObject(@NotNull final Spatial model) { } @Override + @FXThread public void notifyFXAddedChild(@NotNull final Object parent, @NotNull final Object added, final int index, final boolean needSelect) { super.notifyFXAddedChild(parent, added, index, needSelect); @@ -552,6 +579,7 @@ public void notifyFXAddedChild(@NotNull final Object parent, @NotNull final Obje } @Override + @FXThread public void notifyFXRemovedChild(@NotNull final Object parent, @NotNull final Object removed) { super.notifyFXRemovedChild(parent, removed); @@ -567,6 +595,7 @@ public void notifyFXRemovedChild(@NotNull final Object parent, @NotNull final Ob } @Override + @FXThread public void notifyFXChangeProperty(@Nullable final Object parent, @NotNull final Object object, @NotNull final String propertyName) { super.notifyFXChangeProperty(parent, object, propertyName); @@ -601,6 +630,7 @@ public void notifyFXChangeProperty(@Nullable final Object parent, @NotNull final } @Override + @FXThread public void notifyAddedAppState(@NotNull final SceneAppState appState) { final SceneEditor3DState editor3DState = getEditor3DState(); @@ -614,6 +644,7 @@ public void notifyAddedAppState(@NotNull final SceneAppState appState) { } @Override + @FXThread public void notifyRemovedAppState(@NotNull final SceneAppState appState) { final SceneEditor3DState editor3DState = getEditor3DState(); @@ -627,11 +658,13 @@ public void notifyRemovedAppState(@NotNull final SceneAppState appState) { } @Override + @FXThread public void notifyChangedAppState(@NotNull final SceneAppState appState) { getAppStateList().fill(getCurrentModel()); } @Override + @FXThread public void notifyAddedFilter(@NotNull final SceneFilter sceneFilter) { final SceneEditor3DState editor3DState = getEditor3DState(); @@ -645,6 +678,7 @@ public void notifyAddedFilter(@NotNull final SceneFilter sceneFilter) { } @Override + @FXThread public void notifyRemovedFilter(@NotNull final SceneFilter sceneFilter) { final SceneEditor3DState editor3DState = getEditor3DState(); @@ -658,6 +692,7 @@ public void notifyRemovedFilter(@NotNull final SceneFilter sceneFilter) { } @Override + @FXThread public void notifyChangedFilter(@NotNull final SceneFilter sceneFilter) { getFilterList().fill(getCurrentModel()); } diff --git a/src/main/java/com/ss/editor/ui/component/editor/state/EditorState.java b/src/main/java/com/ss/editor/ui/component/editor/state/EditorState.java index 8c7a4c7e..306d314b 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/state/EditorState.java +++ b/src/main/java/com/ss/editor/ui/component/editor/state/EditorState.java @@ -1,5 +1,6 @@ package com.ss.editor.ui.component.editor.state; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.component.editor.state.impl.AdditionalEditorState; import org.jetbrains.annotations.NotNull; @@ -18,6 +19,7 @@ public interface EditorState extends Serializable { * * @param handle the change handler. */ + @FXThread void setChangeHandler(@NotNull Runnable handle); /** @@ -28,6 +30,7 @@ public interface EditorState extends Serializable { * @param the type of the state. * @return the additional editor state. */ + @FXThread @NotNull T getOrCreateAdditionalState(@NotNull Class type, @NotNull Supplier factory); } diff --git a/src/main/java/com/ss/editor/ui/component/editor/state/EditorToolConfig.java b/src/main/java/com/ss/editor/ui/component/editor/state/EditorToolConfig.java index f7a311d1..a0000f6b 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/state/EditorToolConfig.java +++ b/src/main/java/com/ss/editor/ui/component/editor/state/EditorToolConfig.java @@ -1,5 +1,7 @@ package com.ss.editor.ui.component.editor.state; +import com.ss.editor.annotation.FXThread; + /** * The interface implementing a state of editor tool. * @@ -12,6 +14,7 @@ public interface EditorToolConfig { * * @return the tool width */ + @FXThread int getToolWidth(); /** @@ -19,6 +22,7 @@ public interface EditorToolConfig { * * @param toolWidth the tool width */ + @FXThread void setToolWidth(final int toolWidth); /** @@ -26,6 +30,7 @@ public interface EditorToolConfig { * * @return the boolean */ + @FXThread boolean isToolCollapsed(); /** @@ -33,5 +38,6 @@ public interface EditorToolConfig { * * @param toolCollapsed the tool collapsed */ + @FXThread void setToolCollapsed(final boolean toolCollapsed); } diff --git a/src/main/java/com/ss/editor/ui/component/editor/state/impl/AbstractEditorState.java b/src/main/java/com/ss/editor/ui/component/editor/state/impl/AbstractEditorState.java index 8dd31128..be2c7130 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/state/impl/AbstractEditorState.java +++ b/src/main/java/com/ss/editor/ui/component/editor/state/impl/AbstractEditorState.java @@ -1,6 +1,7 @@ package com.ss.editor.ui.component.editor.state.impl; import static com.ss.rlib.util.ObjectUtils.notNull; +import com.ss.editor.annotation.FXThread; import com.ss.editor.config.EditorConfig; import com.ss.editor.ui.component.editor.state.EditorState; import com.ss.rlib.util.ArrayUtils; @@ -46,14 +47,12 @@ public abstract class AbstractEditorState implements EditorState { @NotNull private volatile AdditionalEditorState[] additionalStates; - /** - * Instantiates a new Abstract editor state. - */ public AbstractEditorState() { this.additionalStates = EMPTY_ADDITIONAL_STATES; } @Override + @FXThread public void setChangeHandler(@NotNull final Runnable changeHandler) { this.changeHandler = changeHandler; for (final AdditionalEditorState additionalState : additionalStates) { @@ -61,10 +60,10 @@ public void setChangeHandler(@NotNull final Runnable changeHandler) { } } - @NotNull @Override - public T getOrCreateAdditionalState(@NotNull final Class type, - @NotNull final Supplier factory) { + @FXThread + public @NotNull T getOrCreateAdditionalState(@NotNull final Class type, + @NotNull final Supplier factory) { for (final AdditionalEditorState additionalState : additionalStates) { if (type.isInstance(additionalState)) { @@ -72,7 +71,7 @@ public T getOrCreateAdditionalState(@NotNull f } } - final AdditionalEditorState newAdditionalState = (AdditionalEditorState) factory.get(); + final AdditionalEditorState newAdditionalState = factory.get(); newAdditionalState.setChangeHandler(notNull(changeHandler)); this.additionalStates = ArrayUtils.addToArray(additionalStates, newAdditionalState, AdditionalEditorState.class); @@ -85,11 +84,12 @@ public T getOrCreateAdditionalState(@NotNull f * * @return the change handler. */ - @Nullable - protected Runnable getChangeHandler() { + @FXThread + protected @Nullable Runnable getChangeHandler() { return changeHandler; } + @FXThread protected void notifyChange() { if (changeHandler != null) { changeHandler.run(); diff --git a/src/main/java/com/ss/editor/ui/component/editor/state/impl/BaseEditorSceneEditorState.java b/src/main/java/com/ss/editor/ui/component/editor/state/impl/BaseEditorSceneEditorState.java index 4212f15a..d96401a1 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/state/impl/BaseEditorSceneEditorState.java +++ b/src/main/java/com/ss/editor/ui/component/editor/state/impl/BaseEditorSceneEditorState.java @@ -1,5 +1,6 @@ package com.ss.editor.ui.component.editor.state.impl; +import com.ss.editor.annotation.FXThread; import com.ss.editor.control.transform.EditorTransformSupport.TransformType; import com.ss.editor.control.transform.EditorTransformSupport.TransformationMode; import com.ss.editor.ui.component.editor.impl.scene.AbstractSceneFileEditor; @@ -41,9 +42,6 @@ public abstract class BaseEditorSceneEditorState extends Editor3DWithEditorToolE */ private volatile boolean showStatistics; - /** - * Instantiates a new Abstract model file editor state. - */ public BaseEditorSceneEditorState() { this.enableGrid = true; this.enableSelection = true; @@ -56,6 +54,7 @@ public BaseEditorSceneEditorState() { * * @param enableGrid true is the grid is enabled. */ + @FXThread public void setEnableGrid(final boolean enableGrid) { final boolean changed = isEnableGrid() != enableGrid; this.enableGrid = enableGrid; @@ -67,6 +66,7 @@ public void setEnableGrid(final boolean enableGrid) { * * @return true is the grid is enabled. */ + @FXThread public boolean isEnableGrid() { return enableGrid; } @@ -76,6 +76,7 @@ public boolean isEnableGrid() { * * @param enableSelection true if the selection is enabled. */ + @FXThread public void setEnableSelection(final boolean enableSelection) { final boolean changed = isEnableSelection() != enableSelection; this.enableSelection = enableSelection; @@ -87,6 +88,7 @@ public void setEnableSelection(final boolean enableSelection) { * * @return true if the selection is enabled. */ + @FXThread public boolean isEnableSelection() { return enableSelection; } @@ -96,6 +98,7 @@ public boolean isEnableSelection() { * * @return the transformation type. */ + @FXThread public int getTransformationType() { return transformationType; } @@ -105,6 +108,7 @@ public int getTransformationType() { * * @param transformationType the transformation type. */ + @FXThread public void setTransformationType(final int transformationType) { final boolean changed = getTransformationType() != transformationType; this.transformationType = transformationType; @@ -116,6 +120,7 @@ public void setTransformationType(final int transformationType) { * * @return the transformation mode. */ + @FXThread public int getTransformationMode() { return transformationMode; } @@ -125,6 +130,7 @@ public int getTransformationMode() { * * @param transformationMode the transformation mode. */ + @FXThread public void setTransformationMode(final int transformationMode) { final boolean changed = getTransformationMode() != transformationMode; this.transformationMode = transformationMode; @@ -136,6 +142,7 @@ public void setTransformationMode(final int transformationMode) { * * @param showStatistics true if the statistics is need to show. */ + @FXThread public void setShowStatistics(final boolean showStatistics) { final boolean changed = isShowStatistics() != showStatistics; this.showStatistics = showStatistics; @@ -147,6 +154,7 @@ public void setShowStatistics(final boolean showStatistics) { * * @return true if the statistics is need to show. */ + @FXThread public boolean isShowStatistics() { return showStatistics; } diff --git a/src/main/java/com/ss/editor/ui/component/editor/state/impl/Editor3DEditorState.java b/src/main/java/com/ss/editor/ui/component/editor/state/impl/Editor3DEditorState.java index 6e67f4be..5ab4475f 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/state/impl/Editor3DEditorState.java +++ b/src/main/java/com/ss/editor/ui/component/editor/state/impl/Editor3DEditorState.java @@ -3,6 +3,7 @@ import static com.ss.rlib.util.ObjectUtils.notNull; import com.jme3.math.FastMath; import com.jme3.math.Vector3f; +import com.ss.editor.annotation.FXThread; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -46,9 +47,6 @@ public class Editor3DEditorState extends AbstractEditorState { */ protected volatile float cameraTDistance; - /** - * Instantiates a new Abstract editor state. - */ public Editor3DEditorState() { this.cameraLocation = new Vector3f(); this.cameraVRotation = FastMath.PI / 6; @@ -62,6 +60,7 @@ public Editor3DEditorState() { * * @param cameraHRotation the new horizontal rotation. */ + @FXThread public void setCameraHRotation(final float cameraHRotation) { final boolean changed = getCameraHRotation() != cameraHRotation; this.cameraHRotation = cameraHRotation; @@ -73,6 +72,7 @@ public void setCameraHRotation(final float cameraHRotation) { * * @return the horizontal camera rotation. */ + @FXThread public float getCameraHRotation() { return cameraHRotation; } @@ -82,6 +82,7 @@ public float getCameraHRotation() { * * @param cameraLocation the new camera position. */ + @FXThread public void setCameraLocation(@NotNull final Vector3f cameraLocation) { final boolean changed = Objects.equals(getCameraLocation(), cameraLocation); getCameraLocation().set(cameraLocation); @@ -93,8 +94,8 @@ public void setCameraLocation(@NotNull final Vector3f cameraLocation) { * * @return the camera location. */ - @NotNull - public Vector3f getCameraLocation() { + @FXThread + public @NotNull Vector3f getCameraLocation() { if (cameraLocation == null) cameraLocation = new Vector3f(); return notNull(cameraLocation); } @@ -104,6 +105,7 @@ public Vector3f getCameraLocation() { * * @param cameraTDistance the new camera zoom. */ + @FXThread public void setCameraTDistance(final float cameraTDistance) { final boolean changed = getCameraTDistance() != cameraTDistance; this.cameraTDistance = cameraTDistance; @@ -115,6 +117,7 @@ public void setCameraTDistance(final float cameraTDistance) { * * @return the camera zoom. */ + @FXThread public float getCameraTDistance() { return cameraTDistance; } @@ -124,6 +127,7 @@ public float getCameraTDistance() { * * @param cameraSpeed the camera speed. */ + @FXThread public void setCameraSpeed(final float cameraSpeed) { final boolean changed = getCameraSpeed() != cameraSpeed; this.cameraSpeed = cameraSpeed; @@ -135,6 +139,7 @@ public void setCameraSpeed(final float cameraSpeed) { * * @return the camera speed. */ + @FXThread public float getCameraSpeed() { return cameraSpeed; } @@ -144,6 +149,7 @@ public float getCameraSpeed() { * * @param cameraVRotation the new vertical rotation. */ + @FXThread public void setCameraVRotation(final float cameraVRotation) { final boolean changed = getCameraVRotation() != cameraVRotation; this.cameraVRotation = cameraVRotation; @@ -155,6 +161,7 @@ public void setCameraVRotation(final float cameraVRotation) { * * @return the vertical camera rotation. */ + @FXThread public float getCameraVRotation() { return cameraVRotation; } diff --git a/src/main/java/com/ss/editor/ui/component/editor/state/impl/Editor3DWithEditorToolEditorState.java b/src/main/java/com/ss/editor/ui/component/editor/state/impl/Editor3DWithEditorToolEditorState.java index 5d067c68..b4485e62 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/state/impl/Editor3DWithEditorToolEditorState.java +++ b/src/main/java/com/ss/editor/ui/component/editor/state/impl/Editor3DWithEditorToolEditorState.java @@ -1,6 +1,7 @@ package com.ss.editor.ui.component.editor.state.impl; import static java.lang.Math.abs; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.component.editor.state.EditorToolConfig; /** @@ -40,11 +41,13 @@ public Editor3DWithEditorToolEditorState() { } @Override + @FXThread public int getToolWidth() { return toolWidth; } @Override + @FXThread public void setToolWidth(final int toolWidth) { final boolean changed = abs(getToolWidth() - toolWidth) > 3; this.toolWidth = toolWidth; @@ -52,11 +55,13 @@ public void setToolWidth(final int toolWidth) { } @Override + @FXThread public boolean isToolCollapsed() { return toolCollapsed; } @Override + @FXThread public void setToolCollapsed(final boolean toolCollapsed) { final boolean changed = isToolCollapsed() != toolCollapsed; this.toolCollapsed = toolCollapsed; diff --git a/src/main/java/com/ss/editor/ui/component/editor/state/impl/EditorMaterialEditorState.java b/src/main/java/com/ss/editor/ui/component/editor/state/impl/EditorMaterialEditorState.java index 72f1c2dd..2c5c69a5 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/state/impl/EditorMaterialEditorState.java +++ b/src/main/java/com/ss/editor/ui/component/editor/state/impl/EditorMaterialEditorState.java @@ -1,6 +1,7 @@ package com.ss.editor.ui.component.editor.state.impl; import com.jme3.renderer.queue.RenderQueue; +import com.ss.editor.annotation.FXThread; import com.ss.editor.plugin.api.editor.material.BaseMaterialEditor3DState; import com.ss.editor.ui.component.editor.impl.material.MaterialFileEditor; @@ -36,9 +37,6 @@ public class EditorMaterialEditorState extends Editor3DWithEditorToolEditorState */ private volatile boolean lightEnable; - /** - * Instantiates a new Material file editor state. - */ public EditorMaterialEditorState() { modelType = BaseMaterialEditor3DState.ModelType.BOX.ordinal(); bucketTypeId = RenderQueue.Bucket.Inherit.ordinal(); @@ -50,7 +48,8 @@ public EditorMaterialEditorState() { * * @return the bucket type. */ - public RenderQueue.Bucket getBucketType() { + @FXThread + public @NotNull RenderQueue.Bucket getBucketType() { return BUCKETS[bucketTypeId]; } @@ -59,6 +58,7 @@ public RenderQueue.Bucket getBucketType() { * * @param bucketType the bucket type. */ + @FXThread public void setBucketType(@NotNull final RenderQueue.Bucket bucketType) { final boolean changed = getBucketTypeId() != bucketType.ordinal(); this.bucketTypeId = bucketType.ordinal(); @@ -73,6 +73,7 @@ public void setBucketType(@NotNull final RenderQueue.Bucket bucketType) { * * @return the bucket type ordinal. */ + @FXThread public int getBucketTypeId() { return bucketTypeId; } @@ -82,6 +83,7 @@ public int getBucketTypeId() { * * @return the model type. */ + @FXThread public int getModelType() { return modelType; } @@ -91,6 +93,7 @@ public int getModelType() { * * @param modelType the model type. */ + @FXThread public void setModelType(@NotNull final BaseMaterialEditor3DState.ModelType modelType) { final boolean changed = getModelType() != modelType.ordinal(); this.modelType = modelType.ordinal(); @@ -105,6 +108,7 @@ public void setModelType(@NotNull final BaseMaterialEditor3DState.ModelType mode * * @return true if the light is enabled. */ + @FXThread public boolean isLightEnable() { return lightEnable; } @@ -114,6 +118,7 @@ public boolean isLightEnable() { * * @param lightEnable true if the light is enabled. */ + @FXThread public void setLightEnable(final boolean lightEnable) { final boolean changed = isLightEnable() != lightEnable; this.lightEnable = lightEnable; diff --git a/src/main/java/com/ss/editor/ui/component/editor/state/impl/EditorModelEditorState.java b/src/main/java/com/ss/editor/ui/component/editor/state/impl/EditorModelEditorState.java index 6b34fe6b..31c18f9f 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/state/impl/EditorModelEditorState.java +++ b/src/main/java/com/ss/editor/ui/component/editor/state/impl/EditorModelEditorState.java @@ -1,5 +1,6 @@ package com.ss.editor.ui.component.editor.state.impl; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.component.editor.impl.model.ModelFileEditor; /** @@ -46,6 +47,7 @@ public EditorModelEditorState() { * * @return the sky type. */ + @FXThread public int getSkyType() { return skyType; } @@ -55,6 +57,7 @@ public int getSkyType() { * * @param skyType the sky type. */ + @FXThread public void setSkyType(final int skyType) { final boolean changed = getSkyType() != skyType; this.skyType = skyType; @@ -66,6 +69,7 @@ public void setSkyType(final int skyType) { * * @param enableLight true if the light is enabled. */ + @FXThread public void setEnableLight(final boolean enableLight) { final boolean changed = isEnableLight() != enableLight; this.enableLight = enableLight; @@ -77,6 +81,7 @@ public void setEnableLight(final boolean enableLight) { * * @return true if the light is enabled. */ + @FXThread public boolean isEnableLight() { return enableLight; } @@ -84,6 +89,7 @@ public boolean isEnableLight() { /** * @return true if physics is enabled. */ + @FXThread public boolean isEnablePhysics() { return enablePhysics; } @@ -91,6 +97,7 @@ public boolean isEnablePhysics() { /** * @param enablePhysics true if physics is enabled. */ + @FXThread public void setEnablePhysics(final boolean enablePhysics) { final boolean changed = isEnablePhysics() != enablePhysics; this.enablePhysics = enablePhysics; @@ -100,6 +107,7 @@ public void setEnablePhysics(final boolean enablePhysics) { /** * @return true if debug physics is enabled. */ + @FXThread public boolean isEnableDebugPhysics() { return enableDebugPhysics; } @@ -107,6 +115,7 @@ public boolean isEnableDebugPhysics() { /** * @param enableDebugPhysics true if debug physics is enabled. */ + @FXThread public void setEnableDebugPhysics(final boolean enableDebugPhysics) { final boolean changed = isEnableDebugPhysics() != enableDebugPhysics; this.enableDebugPhysics = enableDebugPhysics; diff --git a/src/main/java/com/ss/editor/ui/component/editor/state/impl/EditorSceneEditorState.java b/src/main/java/com/ss/editor/ui/component/editor/state/impl/EditorSceneEditorState.java index 3cbd745d..0c78c6db 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/state/impl/EditorSceneEditorState.java +++ b/src/main/java/com/ss/editor/ui/component/editor/state/impl/EditorSceneEditorState.java @@ -1,5 +1,6 @@ package com.ss.editor.ui.component.editor.state.impl; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.component.editor.impl.scene.SceneFileEditor; /** @@ -24,9 +25,6 @@ public class EditorSceneEditorState extends BaseEditorSceneEditorState { */ private volatile boolean showedAudio; - /** - * Instantiates a new Scene file editor state. - */ public EditorSceneEditorState() { this.showedAudio = true; this.showedLight = true; @@ -37,6 +35,7 @@ public EditorSceneEditorState() { * * @param showedLight true if the light is showed. */ + @FXThread public void setShowedLight(final boolean showedLight) { final boolean changed = isShowedLight() != showedLight; this.showedLight = showedLight; @@ -48,6 +47,7 @@ public void setShowedLight(final boolean showedLight) { * * @return true if the light is showed. */ + @FXThread public boolean isShowedLight() { return showedLight; } @@ -57,6 +57,7 @@ public boolean isShowedLight() { * * @param showedAudio true if the audio is showed. */ + @FXThread public void setShowedAudio(final boolean showedAudio) { final boolean changed = isShowedAudio() != showedAudio; this.showedAudio = showedAudio; @@ -68,6 +69,7 @@ public void setShowedAudio(final boolean showedAudio) { * * @return true if the audio is showed. */ + @FXThread public boolean isShowedAudio() { return showedAudio; } diff --git a/src/main/java/com/ss/editor/ui/component/editor/state/impl/VoidEditorState.java b/src/main/java/com/ss/editor/ui/component/editor/state/impl/VoidEditorState.java index 27a6e8b3..89612a4a 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/state/impl/VoidEditorState.java +++ b/src/main/java/com/ss/editor/ui/component/editor/state/impl/VoidEditorState.java @@ -1,5 +1,6 @@ package com.ss.editor.ui.component.editor.state.impl; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.component.editor.state.EditorState; import org.jetbrains.annotations.NotNull; @@ -21,10 +22,12 @@ private VoidEditorState() { throw new RuntimeException(); } + @FXThread @Override public void setChangeHandler(@NotNull final Runnable handle) { } + @FXThread @Override public @NotNull T getOrCreateAdditionalState(@NotNull final Class type, @NotNull final Supplier factory) { diff --git a/src/main/java/com/ss/editor/ui/component/split/pane/EditorToolSplitPane.java b/src/main/java/com/ss/editor/ui/component/split/pane/EditorToolSplitPane.java index b2e649f7..17d01dbf 100644 --- a/src/main/java/com/ss/editor/ui/component/split/pane/EditorToolSplitPane.java +++ b/src/main/java/com/ss/editor/ui/component/split/pane/EditorToolSplitPane.java @@ -1,5 +1,6 @@ package com.ss.editor.ui.component.split.pane; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.component.editor.state.EditorToolConfig; import com.ss.editor.ui.component.tab.GlobalLeftToolComponent; import com.ss.editor.ui.component.tab.TabToolComponent; @@ -23,70 +24,75 @@ public class EditorToolSplitPane extends TabToolSplitPane { @NotNull private final Region root; - /** - * Instantiates a new Editor tool split pane. - * - * @param scene the scene - * @param root the root - */ public EditorToolSplitPane(@NotNull final Scene scene, @NotNull final Region root) { super(scene, null); this.root = root; } @Override + @FXThread public void initFor(@NotNull final TabToolComponent toolComponent, @NotNull final Node other) { super.initFor(toolComponent, other); root.widthProperty().addListener((observableValue, oldValue, newValue) -> handleSceneChanged(getSceneSize())); } @Override + @FXThread protected void handleSceneChanged(@NotNull final Number newSize) { super.handleSceneChanged(newSize); Platform.runLater(this::requestLayout); } @Override + @FXThread protected void bindToScene() { } @Override + @FXThread protected void addElements(@NotNull final TabToolComponent toolComponent, @NotNull final Node other) { getItems().setAll(other, toolComponent); } @Override + @FXThread protected boolean loadCollapsed() { return getConfig().isToolCollapsed(); } @Override + @FXThread protected int loadSize() { return getConfig().getToolWidth(); } @Override + @FXThread protected void saveCollapsed(final boolean collapsed) { getConfig().setToolCollapsed(collapsed); } @Override + @FXThread protected void saveSize(final int size) { getConfig().setToolWidth(size); } @Override + @FXThread protected double getCollapsedPosition() { return 1; } @Override + @FXThread protected double getSceneSize() { final double width = root.getWidth(); return width == 0 ? scene.getWidth() : width; } @Override + @FXThread protected double getExpandPosition(final double toolSize, final double sceneSize) { return 1D - super.getExpandPosition(toolSize, sceneSize); } From a45961eae05e4656e993843f95f8d9290442f5ae Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Wed, 13 Sep 2017 22:47:12 +0300 Subject: [PATCH 15/50] updated tree plugin, refactoring. --- .../ss-editor-tree-generator-1.0.0.jar | Bin 83427 -> 0 bytes .../ss-editor-tree-generator-1.0.2.jar | Bin 0 -> 83254 bytes .../material/BaseMaterialEditor3DState.java | 29 ++++++++-- .../material/BaseMaterialFileEditor.java | 33 +++++++---- .../editor/part3d/Advanced3DEditorState.java | 3 +- .../part3d/AdvancedPBR3DEditorState.java | 6 ++ .../impl/AdvancedAbstractEditor3DState.java | 54 ++++++++++++++++-- .../editor/impl/audio/AudioViewer3DState.java | 27 ++++++--- .../editor/impl/model/ModelEditor3DState.java | 43 +++++++++----- .../editor/impl/scene/SceneEditor3DState.java | 23 ++++++-- 10 files changed, 169 insertions(+), 49 deletions(-) delete mode 100644 embedded-plugins/ss-editor-tree-generator/ss-editor-tree-generator-1.0.0.jar create mode 100644 embedded-plugins/ss-editor-tree-generator/ss-editor-tree-generator-1.0.2.jar diff --git a/embedded-plugins/ss-editor-tree-generator/ss-editor-tree-generator-1.0.0.jar b/embedded-plugins/ss-editor-tree-generator/ss-editor-tree-generator-1.0.0.jar deleted file mode 100644 index 7970dbbb576694929c2005e3454c3cc65330829b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 83427 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ℑ zC9fH{9G-R$3O|TBu=s3nhq6m#+%V=yI7rLjNU2hRAEr`q2Vbw$ei-FsnKgb$#Wsj; zV)L*kUaoH*H#3P{XPn4zjduKIyTUl;C~2^l9g$PbID8!^bFx0ZVj5MIO(0%t;WUoD z)=9NK7N4MW!jUkU#1T`ZYKlZ0);2?$xi(}~Ris1DL8PlH@b#!u<8T1akxB0ncbJo& zKt)vHA8;zmjk;S4Yvd3muHXw;#wkRy=@rtxD#Wm1+6ic+RFi({#yXX+2`=k`I$f9u zlj5EvCju|FT(6x-IUZI^Mc32l!Q`d^-6Tve+T#J6{f8mT1sVgrGCx~4$N&Ou63njG z|A zeeiD69@xe}@*a;Pi4BkXj&IHZ#(5FV0@)Y+rXOOD&>PWpf3OY$KViRb6|P`tFrfS1 zUcVNWJ7fx%X(?Pfs9ZGJ3ByvDbH{PbgkKd;HLZ3RKkuqZPUE%=N9w&xmL|9%T zd@o~tl*W5;&StnuuGEjgOqlN+C;p8(F>+fYb(@8UVc zh&|WlUQ631rZ~!R50qOX#SM{yccSn;_$@h1X)sruk)-08f21{)3!RKj;Eu;`T z8Al(lJhIF^*~E-avWI{o(sAEz$<6-U__;3dx7Ytj&lgkgenk(m8xVa#m+aYz`|p&{ z?sqm7d*Gw(3v8_Y%W3iO6YoGfi-wUd-GU@yEEci_1!FWT-h$NpjorULB09%vDExW$ z_=_Z#@9__YSZ>BG%bg%}qVV_QT|(#Hjr3{jDW9cm=833BUuuO-rX_fG3N6GsD$T! zr`BTlhk>ahh7vMB)}5OU1RRH4Z<_ayu{#40wHj8mR5)*1qDMOoR0%Wuq6HpYF=PhF zKl?f1CF)fKhVAD$S@&0mx^}z-y+{%~n{Dto5raxAEqb0WjxEuWjppp+*e4lHn{o%O z;xA?{;vyY@uyZYe7jFuOJ=gIJK`A$F5C83e|0lQsd7X~iA9}f*TYsoJkes|!o7Z^A zS;xc114#!~pIy|KD2Ow!_@20FM;H|P$lF;*tvLntoJuuYf8I64br!~J4hY;0N)#zTJ+-Ee0kO2AAX#b{M8^w7*HEqAU>MwMW+o>@_8 z##z&d+8gYo+%&-9KOE{pG9UpIPNO}KXbh*kT={1c49ZPi_QJ+Ky(wl3Op!%8$QUsa z+|P3kxLTS_>W?6#I+Zm8nJRxPQ*g-}tVDLBfOoSELn%AYAz{BaO%EjOuheFbOr`mi z?puMiQgB@9@MQJfg>0pePwjVxz<5|3ptFH-A)-_JFlU;z&#SdkDD1nQ!?a?xRDkMN zoH#8C!Zpz1q>~v&QSq}lVwlNh3MPkma?;OCDG@s|6B`8d@=e>S6E zY0m=Sc0Y8OMTjOVNbMg&r^7zN(~aYbb>dxWX1YTmhGRXOW773QQ4MAhrVJE{l z|2-6MQPcXL&Bry{xa%wOfmK)&PL@7Wj=4Sw(abq7WKdMi~64_kp2Tz^O%v0k@gyKStpoE2VL$?*p$aqz zGegLLG>SMfx=4bOGMv#!s(m+x|5m!{4d19l+1OB11O&pgq}n>yy6$RqZgw}PT)gnV zbgz>q`TKs3dEM@K-L#wap5}1tdEUGd3pc4=J1t&!E$zW<6}fAWBB`a#RcjM(H>V6; zC7e(NsdbhI)2EJL1+8|BA4wHClxNW&y4dQ@KHJ*v!P!3P=ni1NL9^?P$1s(T@0}Rh zIyh0cVs?$oJi0gXY=Hfx%J6H>;%-~qqtj^KoYs0J4eT79?%pDwt_HVrdSva=!QzJ9 zp&abo@}|9Ido8xD*yjb;Gpk#bp>Ef^dpAA?dTR)76zJzUIx+YQlhgg+(wzm;r3Eeu z3*S+$dxZFVLu|V#K#z=fUs8I9T#p^2t2cP`_1M<(Kuvr$27LFZgiV)f=u-}AcdAvK znMS@gxfc8Qp30-EBN6)5#r8|jx4XX;_3?W66FgxQ8_qW=Yj?^HNpP2n*Dh&vlX`p@ zFKAa~>K5(Dt?ljCE~R&uSmv$EF9JVta6TFkH%#x=EbuqHG8n;yr|lVh zq_LofHZ^d5%ezrw4t(_SU2-RWgFAA#uVm*P`Z%En4jN><_|c#T4#T^)ela*cRf&+# z_oJu8I8g4pgIA6+?`3j}v?BLTf7d}z;>;A)`WS?p%4bV<*DLIQRyXf5e0Cty4J zao}D3gmO-O*b&xcJ8N}F6ell3fePQ@%XnL-ajzdBszwYLk)9^5!RLXo^6q1etC4@h zy1(>?4rSmyI|S!z)Cqp!L@_%&#l(~xQrcKt+uT^}5?wLsEbPO?xDz2Eghz!AvmL5m*xRKG79ig+7*m+AL&6$5?G**8(5#e@b5X4SE@?IPc62*6+j&f8P$ox{Mv zfzeT{yOp!pzE6u?KZd#M{OQR=&1v9P(3b~*CmAhWzVkXI**pkFr?XcOJX1$i;2PfNobGnD#i3SN+iN{v-do01m3g}2a9D$ zp;3!<23=129Y+~~WCjQgh#BuW;I$@P9Y_lM-qg;XKtEtBq@9L`F~Y*7=Uy4&>;`@P z%p{WSHR$V1v?QLKx87goeumAjPg2}Dl-{~uWVLCHx0^wZz^+2m;jg<5td%B9EcktbGm6CJOOwql z_DBbae zLc7meT>_N?Q`N#e5C#%eUf1naK-jQhf(m7piOYzP%t6jXY(*j$Oq;jDOs)g~m6U5v zL9hCBqcqpF4hdj=MEMO_u&oG-yqz3t{cfbDq0t7kz@X44biONzHBwf!>>Y*ckToT^ zFX#sHlJmL@T1-M)8{5VVnlZSX!6qk|Taf%41!{_We~Vb+i#+}#OyM}QD0Z%@km^tS z0FA#C;G02(_2%@(m$*RiEej(ZS}z|}ZTFqvtS1@mh(ck$7>XQJCvFKPzPI7cBSzP0 zvU0urXK-;InNOA5K-LVemEeBUd$q*f!lqF0zbE@WBf$lK&WL*5uai+0ZUGfHnoP+C zB)DfmqF*#EYFWTg)gMX{uc7_B#uXWBty%gcLi>nEx%RgUc_wh-AkSWdTnD$otfV@+ zFLYHC=Mq_2f*;LfGg1sU0F+8_-L zBJxm(Rmq+Qe7pq-ZTL^QBVGcxp69Oy2%33kj06B#<$0{(W9Vxp_(7GH`jpZro~1;% ze*}T7_<~lr2E*L<>voUk8I=x&H%&p4Ly&i7V(RsbVnJO?Au>S4QF13EwU9Ga&o+Q? zU)p1Z2Lj0%`H^6Wo+M=mPQ$zZG;wuJE*uOsL}hRxCluiGq2FI2`whJ#QFqC4%zdrL z4|0>=S-4R!s>ISN^~G=?Lc?-65U^)gDiLQk|CqUOb2U%2>bA4 z4nyi8X+CKIrTUf%^>_2K)Lb}N;mPRtt<=fAc&F^|VtsE`~Nk*NfpiIaBZ@EO`BkMJMmV86=8 z`44lkKVg#m#(SCGD8HKfdl}zaub5ul$i8#F^&jxxER2WNp9+?@HY=y+2W}W(79d5h zni^!oD>qMDZ5dwW$i6ea1CI`98Z?u>C;XV-Za;%1^z!I4KN0AdUvtU6qjC*2U0T|U zB=kxa#~P)-*uO`Q%-*8=zggcneRq_L(k*N~`N%nqTV)4ei=|cav`eUzMZ&#LyZ&W7 z>3ZTn@F-y#hA=07MSD{@2MH4@p;jio%6nc!*e3$^06f2g6(=BtjW|be81EpcVkZ$9TEz(Ze>S3Gbl%tjm1sr?IQ_G5_rO2kir9IKR zH|5dOEiqTrCK>f~%IKI>QZyCvD+GjPz|=A;RF^R=i_3YS`t2>62(<&yVn`Nuh#CXpv%JLQwMLLep z)|&V&!H8W;^y=JX#){Y%vS6G0mD4I?N;(8}EGk5ojmu1xn#)v`n&*{5H;gN$pOVWr z`dN5OYYaE;1OsczYtV$hi@l_m1NuZ}7ciAv7L=r)m017E?g{iDiPyU>lZLL*#?czy zqFZQxC4}?uy?F5M*z?Cy>p~(j4zdF>jcWkE8bChG^StVY2cY%T#ybC@a< z!}LF#6U7ZR1sN%6xMUa_80=JjLtO2{ZdH;EZ5w$&bOZ&|SFxtbgrL0=TQhlh1CESI zyJpYg_WR0t85HbI9!!?PoF$wVYOXHqX72H<#^^SekDBSYhnzJNAeoS_{zQFvYHGb$ zTV7jUTU=e?&neI;oA|WICl`O73$XfF|-0YGl@V z9{7*>6bV^y?A@YsHN!O7>_(z@uD?zmSMPIy2R#si3hLAmC-qsd$dGW2t~Ur}?Rbq8hqUBk~P41aZD>Z8I_DRR5$|XJwSfZZC&P+ zZaMm*o4Ua26J1S)qHl{iBn-xJv|{C2tHbO4?;5}VSevt?gCRTY1m~;-M@AW1CD2na zC+2Xi`??gHGrzW}x*i?#wKpT%(~}Co%gVt*ej7L1Y$3`q0R;jr)66WS#5I@CDTPB( z)5l_kS9n69negK`u?q{`h>XGQS>0}0o3Z$AkK`@+^5 zRQJuf`EFA?f`ei&YdaBe=LNOyQN_Zu!D;L+miWyAHQCm8wxY$vh~M)%n*O75)G*#( zBHhhZdV>0I5vFkEh0N^EY;k2%|DauHe5gns@VJN`|Lon*+ zkgFqy<)iZSq(Tz9tt>t9zbE=gO6_=jsaiZ*o7vU9f`L7zbPESB;DSO)rOz6cW8WXO zOQ4fIQn^VT0!4K}e^%$aEs{P}8mA zS)mq6Z%E)^uQNI4j}Z{uiz@=c;2UBPwtzR^7w+mJxl@O_+cngkQjN)V?ZV&=G)e`#AhQzSJs42-R`YB?wDFs z{fqs1cpy>BE322qrx2L5BGf#)!Fya)Iu z1%`JrRJ<07Q4rREtchwvH&XEIVi93gLRJ4gL6&t!O9U+B=VHNpVj?-#^UCDx0*pR&tU6+y#P z4u^sNSPsQ%*(sYjeTLsnIk$P_m8~2A9&zA3MeT&aQ+lrnH*yAgj>nB<2?Dfk~{Rf3cQWT z=)6Ein-OJO6wVgg%ggD3Z^<<8A|cmReD6a4Ethmi#+qJ2;C70@8wj%e7XhvV1Aago z^xck%j*?e$WJP7%And%Bwojm>t1^FhT;UO)V6k}?QM-j$8SJ*np|UQwEb1XkcL~!4u?71n)c5w<| zmvqEHtas?F%-j|gJ6{?ggHb`ix_M&9wi%5(y32Cx4#KVL|LfHJeD;qP$;d!9YutPn zh_Z@P;>mL7N&Coo*ZY7+NG^F0g5rJGfJ5dkW%pwzyrO{&`kJmh#0eTSHCSefR_DZ9xe4gedQj+rNQ;Uf@yd&0BvsY39>9hrtwU{8x^?4Uv|ML zbN?N5@c9YziLtJ>aP~k7C~Y&q3+E6$@zO(6S_Ir@CQs=V;VO63uK1^TLBb9x(AwX3vZgH4gV!fI*tnXFxtN`d;GrE`V)CX?twp8HZvIn<=H^{aqt9+(D z$&+gEF-iZ;SNt1M6u)l@0>}@P8Luyz*q=WVQ*008&^}OZup*XNFlSCsH!1_>jO!iR z2NlB(D7JQfwYhmKr7pGe4Esrjq7d}&3++2wFg;sC_SS6Bo}dk}9L7_Tz%Juiry<5J zt<9hj^H)ykxv{YzNgi|#L`~ojgF=$PE_ME`l?anNps(kkr|f!C^#kFz=%_h6KZES z+Krf{{7o>*{R@&4A8-IK{Mjtf7fK!;#1bWR|NhN#eV<55QLzbK$*#liEqgMM&T zuJpA-a$Wbp(rr8r?P{(c8!&x@q;UO?tlWD@!Row{lLyBQzj!QWamb;g5QTdp8Cc9- z&QvLf4f<*cw?&8xIh*}}S4x+6g_f*k34{Mb zhodOFI^W)}xVq*Q^}$-WS+eD?DsVk0?+shAgMhj#${&RNg_AK#zkkvz*yl%OBIiY7 zfEDMzvnv?7h=}4V)+;AbL*_(A>UI{pL+AF8=@_n0u5kRbabFlcEfT??hK2ovQ9QXs z5{@}hD9z=#BfX0c3)xLNOL5UhEoQ%?lJH0xFBx>ybljr5emhs{XW(&|f(4o;J@#M) z+kmuo&f6BqZoBp`6Gh}%+0D%6z3`{s)0_Z+j{JUkfZNXkJ-~{b_d<5fk_TR~JaEF} zfL-1bcqs|$u?vJBOweITIhf!Y^Tty1^OjTdXYZ!uuGG*7w7k+(?aPTl><8V$snhx0t)ZktAbnB!oC1qB=3G*!!J1S*$#28u_nA zdd!IQU&t3U78T;}Z&5$DBL%^VS6Eu+GkmlDKK^q(PRBUKY%f@|BRmdl>0~b+W{I2p zv)#IYz^V|{vyjgWnCaR2Ib#?en868M{YG7VyAxn^z%wx2n@IfOn?ToaGdh7qJDw-} zpL|@_(s%%VF_Jl3_+5JWI_l8@7E?RgMs?xa)jY~n%RL85`rMh*9n8x6(1w{gpL0ry z>Hdfj9_vvBtV4NTTTfPXt8n(g8j$&Mp|Iby*jj1_+2SoYBfwWt4-N65g40M)SA z^?*acRuzfG^zvED3=!~?pMNh)5c{Z%hQ(na5!wr9JGxHLG!QoJ^$iR#n zNn}`yed$9cF^<~dcR>N2O!bL_cafr%T*Og0`FE!Q~ z1fvX*2z>H}!J~G3JsyxUXIg(V!eA<1s{W>5ZDx-+5HFrSUyYZz^s~bMj3ZCc$mG!< zv~$F?_fw&Dp(B`#Y+a72Zv=AWJq~%56?Hro#m4M$N!0p%pkmh3YsHbqv~~Mn?tQi6 z(3H`%$JemYNF|xPp5GA<9NzH<3`}33Ixso6Xc6T14XAizYm@-#o7#=(r~y4f*tX)5_8; zSnHw7!?8kQXvux1PGHLY=?|!M^LsDcNxzP$2NYt@Ygg~8KQ+ysa6e`LJzMb2kOSJ| z?h5}$kN7*bty}UR*Z}+wY(Vt?e?^h;{{aqc+9ER|^4ij61;fGR|IwtQb~5kaCP z_|{=bWz61$Rd~I8wHh|jiQ={6AOsN_*@JHQ4n=q|JhzxoKSOixbi5W+GOS70M7(Cr z4P()&OB`D%HWSM9d>*xQm>a6c1RSn?^Jq-dYg*%4tjArZrvwA*l)U+60@P4R)5jw& z@lYyc$bLQBa}LRyeNdvI0{4-TY{-Q{o-@f{5H8Y-%WOX!L{5~Jx}!oLCi-MqM_UXx ztP|U?8V}E1RtGQidzNCE?ly*v_ExjOi@b??q0@cg7`g-}e+k-WkOTtq?JoA?4RhNj zp-nY4o(Pd4MK+493xWtDb5%}Fco5d0-SHNmG78bt2SKA* z;4rHXfbfuI53nGaA-~^JBmfFr=rq>T;3wAJXenO3e=`KZ6fL%iI|is`cylgL9G8s| z+1C8Do&L8noAghk|IhCGe|=>r{#RSk-oVk|r)+KF==7iX8UL|P*^HU;`A>oTKdf>2 z{}0yrL7rQh7&-qpYV?2I%W76m*lL)*u{2gcB@qtEt0D)SQX6yQ_2~|DZA0>WiA+$4 zb@+OTGMFZ-H)UP54c5w(Ltq&#$TUa#IqW8FXy$(d5L`kFj8o3<8Q=Y|`~e2(p{D?y z$qsc}fbU%URnIBs>_->6#P{dxEk3Y2il6L(E{{>4EK#T#mjecjWs2Hh0haOa+0e~E ztW-t&I>3MvMMb`GpG*O^hs%Q0fqa{$!~ymlTrw0u&|ebbM#=znm8)(gfUwz{FU0OZ zi_#mLeol6J6|T#Ck2K(>FYO`mdXp4WyqCtFCGAC0b)7OLyCA8Rc2n=p+XB71oof&( z;A;|<-CojhLC0lcMo=NgRfoO~WYj6b$#3QnW~BiZ&3n^fGPV0{A@gtGxn#X~biZhH z`>EQ6!vbm63#V&+>d|<-@p@vE?A}|d;{cVo+I!1Jjj~L2`I&Qjtf!dAc&F>~eX)N! zx;VgIo1BAmE)}zb2$BDi_F=jPa5!-;X(90L#8#dlH8Z(6Ow9&_x_oEAqwXkfaS%(X z^U!UQfp7tdJ9Bn8N%|IUk~YayCtK+-;d&kTM2=F42Yr5$W@&<9iCW1FSJIVlrU2fe zRC-`udC1vL6~#QuH*X(EQKBOxrLV8G0<6;*knLwXp*yd64)9viBt3+QO z?o|U}EA2bXkV^rPoF|5Z%%Zf^0dTRm3e%#U58VX9o?ulNQ!FZelyIHXT3`2{vNPOp z#U#mkv}BG=>6AD|I@<6>nVqB*mV3`T9eaqOp?lm4v9_y10SbzDHO-dKHCLWa zPN23g8#iC5CTF}<`Q$mw+Yr@L!saM>Vq%O&3YgteaLi~!c=@>|_ShXjq_f-$bs6r7 zk@-feNq>U!v)(Zy^N-T2+mq=IgCX}0)q=)eSKY|qh>Q#&Hh}xu9I$rHxV}&_t@dG) z?F?yw`x+hm_NA^XU9v9L?Y7y&HF@U->1Dr%_?#c=d`;@R@4RUSerd7qSt;?$Y9WjF zv4rqpude*FI$@0XyQud)|4<`|d3gxs?1+?yTBqa*BtzAWg{6L*$C=Ps=cK5cN`ikl z7FkiDGhu2@781W(NC}O*w*ATjC=EP~Q_dxgrE{ZXlC!jGqNnoQR+lkcbtYbixm&$7%AXmjTrtTW40Zb=fT|a~FtKuUSx)Yj$8{ zj@Iy0VH3L7=j6;<2WNA_0(Uw^i*A=B?8_ux*@H5X3)J~@aXyfYo?<{r5^ zeZ3>VkyeXsEP~?;q*;GE?+4J($HrAUTcpDjk*rb_H8aZq1>Ql`FFZ0@;Bd|vC_XPS zAmi_VwIuw!YqEp&-g~F0Ho#$Py!<0Ah7vJ;jwN=~%Lv5I@K?gl>)_$_OH?lM=V`H+#K&eee=4@?#*vcy~;8i?9=bx@$P_5ogW`` z9jqee(efe88NEIXv^$LN;pqF_369<$4ITlKa z!c67MMz1)>TrV|N_5^81}*9OdFJW?^;(Vel{VkK0)eS1Z9YNqb<<^PI zyQ+Km!Fcfv=%ViJGnBSpo5Zk=v=1e_zPW8Krs2U8R;0lbg;8Fw5R_m7)Rl(35Tl58 z$k{-4Q7gguaOn4?o}}-)WP2_W=l8F;-VJGlhUvTIs*L<_EEkh{%$$;B@y)fw{BW{{ zKEgpkVU@wU)lG6cMIWqK;iFY?F@{a19=_m9E^&IKgQ>l-PLnFR#dm7=#>4-`g#Sx7 z^ozf0B+k74gZ(i|Ncs})DspR7Q5s_o0sd27xtBZ|^5@*Ug4{X|j1{|(X0e;18qeyCH976#T%|6?3& zR)=y|Sw{LI-|VJuhvZv<@Sq|5quT-jTH&83p$;np9100`;h3~Vg1&R2#bT+_MAwX3 ziT2_H;s(YVuBCIKX>+MaU2UVYQBkea^g{;*IPE&_qQ(3EBY4T-G@F^q;C!5&@|pE@ z`5ydrA}>U#x?iEF9b+|gsEN9ovhTUI*B7&<8EMldF+5FWtKd)>xrwq>a}cX&-nJM3 zzg2A53BqmPn%*up+)V{OY+oArp?vjw@I!?s??3<*q1;{l>a9H8XDC4IUY!zebXJ*% zuq0RcUU|OXo234;31oIpqyEzgbvEMoROM4}m|o>mau|k6SNT3MGEL>P!+hUq_WppH z%D3=91+_B@-qt)`^j`}~@1T|U!I2;1#|P6Vke~9rM(FB&Xk;_hO1V0HQ&0H;E#n6{ zs-NmTI;ve1+->r{jy5%(AMN1oU5@*wPTLti^jG8CS#|!s1X5T^o0U5MmShzua+j&OPP zV+|P!OZdMS!TZ&wedcj*ozeECGoFw$EBdWBTGfwq`Ky^Uk$DZ+=}pz1CxrK_ zf8cm09{6T3>#8^ ztz^7d^w7qE%tUJGWI8l1-+Z96(^^=^XM4g!tAOlOWphCcg$ls!SJ(6F3l|O^rg5%c z-^4~fkOVWKG*cB=QxAK=oS$j>lcj!)$+Z+J#?&fZfn^!i_Bt^&C%Gf9=^e31BLl;j zPvc`s^4MfNygepfqd{29N=p#Z^_0E!wRQ|kgK0Movgm}2M#bEoG@|VAuN*#Lf(_V{y2$5;Y?#F-x8&tS; z?5!7$Rgag~(1$G&F@Ef|gQNG<-D+x2UjpuyW zh2~SfO9+hPv}!=D46L_j*DDgoJ#;Jz^dwXrbaMy}lwwu^S$G&!jm%5Ozn}E)k>;f#U^NW5i;bsMpFH9U0$_PVW#R!4}3x98kUb<+`k2P}>3v*1p zuR`5D=8$pMHd~4yUP0tAFapH9VEpc_Bfz_rI)YR} z+R_T@84XlOo*V2kFf$+1Bj4Mz7VXR&B%YM;l~*f9j2@$A9w=&?SvF6SY5kZb4u)B< z0;6zq6cgMr@IV)LQGeBJTg>CdOQ54qD>tSK9|f?gmX+4JhIjH5PrI4v*GJdOG1?~U z-NHlFCRl(yVp%d3ehZwkrE`(-9ExYBqnDCG1DBAU^l)1Tp1j_f(q6HLW}{>gDTL0f z*oOs_%NL6-ns*2?r69E^}Aacv*otTVU=jCF*hQLfc zcXftY@q~x@%tpgtk=r1A%2IFIR{B*nRI=@=YKmmic6u3+F;Ao)PPo7`Ps@14-7CJ( z;rBAzKiH(JZsD052{yBPd$J`plT+VW`IzNoZ>tom0naiS*DOoRvS!fNg-o9VimRtY zcw*x-g?ap21$b)gw73XlQfyKVsKv^^f~jTMjg_I!GVo$|CG~$T=WbKxuGLqT#-;}| zeb;%~G#~52!Gn*bxLQjeZ%{I&JnWPh#t;wNaFUE(IzZJHD0c{5jCD)a_+*9U5k`^4 zdT%ZnIL^b`?_fGhj;y6@Xsq>~<6%>zo^1;=_+1X_<7Lrm&aO$9;H!ycK&hP(#&K_T z7nj_fnkPJ1A#=+bK;R=8rjP#?2bzt{F=cs6tH0y6IMi3A1@m&SHS2ieL@ z-2@nI<8iLs1{m?@9N(h);*BeV^hWFsFLMiQ7W)?-)oOtqn6dNy)yA17o;D1OrSnDZ z(D)O8D9;gMrjql1#)s0h0$Wg5y`s?sGYn>4yBUxuK+!C#vhWzCEfRql#;SBhB|DX@ z$xK?4I3%6HTG)nSRq_sGZe^7{=xW&O#n|F(hJM zJ&h^c!SSp@Dte?d^7kNAjUi)GaXXnsQ0Yhr7hll5wNOBMHj~ z4xO6cQf1`j<4Frg64^sZOG(h_qYO#8kDU5weLrvJhDh2Rpi1-MSS(H$v?uGu>LcH( zc_G9;oDfRXf#!n|=A#-sUVN$tjyR#!2jp6!(;(65b5VRPD-w43_+-$GDY`@8c#Ps_NY+OR zjH&auclTwWR_D;K9z`Ve81ZvZWckc-Z&J*8O4&f!nKWlhrUIpOFkH`=bLB3c)tJl0 zrhSw9jz#@eC2+cpfW~TUxzPk-po~zG*c0`=dVz^Vw$;$WA{IBtLhfcpT<+L!a#tL$ zplrMKx+I?V(e-%X@mCa=}3ZNMUoOF!|Du_8qAPQCYy%7L>JR8tvg|hnklHFU?VFN{Jmno)1MM^{H(J*fyfG z&KlTgm zbA&;DE5^c#iy#?!6aYMuj-!R{Yl@eN&>;t^^-CWggD3oM!kioCa^5 zv@uV61d#Tmj{=%^=K#n~nMw&3GdDkTUhUK6V=Sj|sjsGZPAfh_PO=jgvE+*Yx0ilE zl2g{Z`!doZrvfKCIDsSNt^9k(-l?%(X0zx(+b|OSngzeh&pCt16$VW5C!M ztLlRB13^N(Rn^hR(LfVO+^Ny8+`C7m=n=EBq0=oV-vBaiO1O0Ea%rr&sqq_@^CnYS zkJ3%9^*-*kT$i)%d0{&z$~@2K9|+_)YR;fT~U-a+GD;0G_sgi>5{ue7=r3 zjbJqmw+v;$z&e14XJ(i(khfu|J^9?^-X@TbMMMp1&c?r^feUnVXJ`%EFmxW=HoDU^mzM@aR~O zZWw3x1lvXaW^%j#W8fJ!c|i3=pSXl71mHIC>&=RrN6#u;bxUGRt}??%jfco)_W77` zzGNV``vIaMWdx(W=4G_yXY(vLa!vJNWJX1f_6IblLz>XI%kW+laa|pI-!ag)OUr=h z=e4^8o$jb`32NQp^+LviBdB+#P5$xpkigB3PM%Wm^pp4g$$12&d(c`dfyrnDzgV?a zuMdnu(XQJ24VFgL28$z?wwQ1p=M$a;tWB7!25rn#2ID~7vVSS0t^JBun(*lxx_cbf z_675wpH{Vs%FVlge*Fsk=^f(vKX?+Awln_! zx9OR$mC59uLME0eQp7Z|$pK86&VP-JNO>iwYRh%0vv)gG80D{O=6BVJyc5X zRV|Kp7@s=buBzCL%esFILF$mGX(1Y|No=rb^tOg7HE09*sM55*x_x=BteIr+WA3y|B&$f&0x*Yu4EZQQB!5zp;GW)+u zJMVZZ-?)#<$O;iM$|ig7y@kjK+1Yz!W$ztIWRqi$Y*F^!B(k#iCR>E(KEF~?PxL(3 z>vf#-hwJmczu#-G`_h5hyW*DR^eSyn>vo3}tC7#^ZMrh!NY`5;7#Vxj#BbFevN&&K zmKt`#r;02{L7Kj7o26GJPc5w!oyR$wD;jX-`V@d%;SH?+02eDl=vbK1SzFV6Kfz*S zrKd+{pl7aU_1^{VzYOu^EG%q(&la-dl8^$|6Iix-?zMLe_FtZi|Un8$=zDtk$h{urZCZ6&>yzZNNj^%vbVc$#BSD7Kj&^ zRHR%~xfXNBHABZ-&s2{@sMZI*SO9e+SHKxlv|*upFt!)Z>#Nrqu~!xyMoAbue=mlB z@ZHV`C8kZ(_T10*YUGJC($#@%#W7q^9bp$n{gs)8!`?NAXzR5~rfS!defS9uJdun9VXg0QR1GE5OitVX(>S+A=b=yt_qVKuxDo-z+Cdg<#K6 zI+2&t-6}kXc*L%ftLIuK)7}5D%sz85-fq6jj8CO)v!vkbkzeVCw|THXrVFclYzvbD z`rrZAq`~}@9P@RpY7a`|Yjnre_P$J{}?WyBr|ZzL_6Mc8F)Mp`~CGPZq%{-ZC|PbmMb*)?TI) zet$!U?o-hgi4&si)t+a`{*ZlZO3+Hy*)b@*yO_b)pkla6arbz+chvf@soS{&hGP(C zb4LhaCbun+GqU0TEq9zw*qxuROMPHsaYGE9M~BO91xsT3V>dJ$A;|V#%V$H-#C73h zMVVQFWw=cI=B|&?hCH~dPyyrJnl;rBnW&y98LtkLv&N#;={B@2hz@GEJ~7RYS+Ctb z*;+qDfvBoaMreDh&_rS$JK2RE@1j|!Ui8ku7hmG7W*w9-)2Iu`X)~bPDP$Xq)(p@8 zrl+5W)jmFG?P^<}bA6xdV5G)V(1;=rjc0e*wH)e|Px=F^TV7Qa^@l{y6VQ->y}3EI z+A)VTA3c`Z(+_+$6`Dhzy|IJRy6f!QC^fLW0flYD4Jv-$*!;|S7^y_4nK0xFbMsnz zy;cInN$Q$@wuzsgUr6DSC?$?FpRBCFQ{ExQ+o9|;R2HxaJv;}j9B=C^8J_xq-li2J z^kduMM89bX-!wvKe@{`1^yC@ZS8aKtcei)BdWJD{W5kD65$4||q6Y|dEnt4Pr zy!UQI-eY;AChWytN<;$vu+U_vaogB^VQ$HgyV5REF-tdkPogQ-p^Hw$;zd=9!q-xS z8!WkYbn{<6*tF8&D36e&Sm(84IMG{9A9cAOSQ%l|GyQrim|8B(G@x*>q>@z#J7}TA z8hQ}Rb#M!6W-$GT9`~{K;j7fd)#*GlU{}jJ@JGJ-t;8@=W$styXkX3~qmvw*O-kBC z1rZo|1?PZ8wEir6ILAjt(?i)A)6N)7`+<|S^Lu>to|Uf;s;nL;Sx(xjQI^#|)7eCl z|M>12F`Up`T6<+L$MexdAD9Cny51AlE4`wmn;AW{eoX9=)~MT4M!W`Y?{M1dS4`|u zaCOD4lI^l^XArb{Tg3CRLYe0Dh&mQwoTaee_pD)PAu`i3k9)G@;S&z1aR^qu+eC)( zt!8-at-aNRDq#T^-h3;#NchS~qDeMMynIHijVq#(CGO2dNhYEOQnjlqZCLdyZE#&f zFHv7KbLT8?i2K~bO*FQ_9I|Rms}2g_)TUeVCu1EfGHeq$z+6yjZ|?DH&VHB@CCZu} zAHeeAD`MjSYq#18lI3_?hN$~P-;E-{j8yXdnqKUNj{~2jRu(uxL5WF^i}uMTa|e5y z8}^fDpXuJb5gS-yMV&b!cPsT8CRIs~1a;^Z4G!^4c#l-+b$9eQfsfaHl5SSVHi)rg_@#>{HjCUa6R% zA?L&0uTyd+1gOnH0KN61xY)wDl-otJrq zhibK-CQ|dDw|=a5T}?v%HF#i#RqD)7@h}rC#U89k5994MUY6}EGLTvQ zwf6}3uhdc@+|S7PT(>ySFo8(u=?^bWH~-vsB5h&1j1raG=7n85zZ$G&THKTjf0g%< z*4(Ua5kD=vFm`3M#Be#)O~}DnQg;_|ghwW;fZvU3 zO0CH(gJ0bP4h=vTdeFm7xcdWJ3;Q{zr=K=sbq{bVl;op2QsG&%zq2EC1{IMPdR++} zdd4yJ!974(9Xo(ER`hLva?KkAlkxdmPgS23P4%BpwYS>fiBmzdVJxs{;ldI(wB{4f zL^vnpJdm(zoZ|m@BieO_C0U`)T?-mveKgYmU9!A^suhLmL08%&L`lg0^rPvmyS(n` zb0|}AvKSoRhQ9mkqLXL{Z}lZ#5YiD)dAz`-wnHzi(W)TxB#nw0V}(+jdnaf!85pW5 zP6i?jLxD8i5)wQsYA1ROQwGJJH-+0AGoP6}6 zrht;Q1jE1*qro3~S|p90#RCR$nzD*YAgUv%__eD6+n{$2Zb-Szjjt7&Q z4aAKMHq2xsSW)Mos21`+uQJ0q1Bl?lf0FMBL#D1 zV;{kN%73lzlNG5YH?6fp*Lj1;TN;Pi9P`uX(!NO=2Qqdn*=v4xKB8}Xy9}TSJibY- zHOomWEGMl9(>vGJmdyHl5C(k;mY#O+ zJ<|tSpS5O|CPwFr4D0MU6r`QwG1&L}%a**d`i_tC_l_7dGBY#b7gROjqcX%2c4wZ< z@LS-j_ANAs3Z#USr&J`BVz3A_8mWr2P}-V^O!}Cv3S0W3f#`snaU)+YVp3nFD)s=e zmMa8a>qB&k;uEYoY`qz|8K@ao->!nhEh2=6^GFCP@4Z2KRGZZ2f@vO`?MQ_@-Rs&$ z`)KB?Dz*y&?9ikY1iW7hRMc>pO3IMNv662=N?O%m_B;Zg~vtjU!$mkqW77)_M;+DzdFFJI)185HG*fxAs%^qR-(+ z-z0S?9aUp$)>5mK)Z8=Mk;Lgy?8;X_cA-Rl;($+D!^HmT8UDj(W% zs26*eysrug&tV&n5|pL7=byF9>xA+(s>E#w3Q@6?uDRg4DodjSs%R;1|Hq?XJaI;W zqmeu|p(3TNOzDUbk+ofAWzE~uh3+(}Br=v50|?$rpKL|SE$gwa`^@iPIi?xGWYErS zTN^9z%hbv?`>JUu7SSTTkoW7@^We?+P^78k64jXg=A)to|D?RxNQ`)qJW@N%3mmbO zow7;U)Jaw4r*iEGkTk2CaO1R7(}G7=o{^!xHqRZT8pcO<=TLK#HvSB6a%Um0yJuE0 zC?=z)Xl~F@kxhqXPH)WSgMy6M3@<^1!h5tb4A}^R5miS}O7iZTm|=8PErX9U*?6mL zQe`d@!I}zqhkB3WkRm9*$lLAbVNbGrVc5_A9KWQ4;GShdAo0-HlYc0ZWgK?jFc*<5 zBYXM7jl?J{w|bdo(;Miw9uK8u-0R#E*&+trt-Vr%{#Nlzg51jv51$8)UV&^9VJtb; z*{sbJXte}RsZvv@HCefqYsfs*4Gjjxtpj51= zE;|RVtJYwrRk3!zUjaX&>Gn-f{${0!uko-*j;sl3S7Bbx?&fi0()zq`xPs5! zO+*1R(F7kKS_0Knx>ce+XN#Wg`I^^&@#f2xA}vWa$;=MRucRxwux;r!PeU z1)$jN#;V%=Mo3`7pFZrbR?myzGbz0~0ZUHbVPtJqLnoOZkD7c#Cabje|qF#T=9&KL>GcqZvIA|PJ!BvK*b!xAO`BILf^c!J#@dvLgxXE7UZQsb= z$_Q4q0Uf+0G>4dS#wwbbEhmHR6qxg@%jtuL>EomO^* zIzEUqDj%)5f24<{!n63ehMk>Emw~hgmG%m5PJHU5vJj*!?&e2w5jyu|`l{#0YxKrX$&PN%22LTk=m{vjTga#(y=%!oE!Tt@ zmo6_tH4>)2VMB)sn!I6w5M14T?0@IsK{9O{q~>Gj>9L49PHM>P`=WRzS6v*@uF_H9 z%y=s%3`6iWsX7#FK3Zu&6J-mywcH>@NK<96Td6?2KIBl(j;9ZG2t__`Yfnb@_A`!{ zCa+EEpfqHW_|dr*hlbb%OY;mfRv>|5W(e}a(y!lP_bxTNRbTLWx97e!@y?zbDjXaHJ%%D0?d`~<>ywYAoL7>*vUwTu^MG9G|Ly!cpOO?Gm*~2WQH^elX z(3NQ1a}b3V*^SX#$|nR;x)?r|`{---`o^7D-s$}<(S)*4OKBK;>*GTnQaeF}y@3j-_3M*Ko=4cl~WB9wiRqJ+pQz__TO_7GH1{*vx6a$ zYat9QMDX8|fT|7T!`!-Ab|R}``wSMYSI4^6K4UMdOrviZ6l!^V&A~Q?8D?&luu`vUPgi2318D-n0o;01fuP#ao zdg&N0v-d_1u^e8>fVHQu@iIzm4!^U%T6Jyv1g>s;yJ00#FHsDMf_UkXzT*T@KPr}Q zs#qvtdNb+dfMkpFAg7WdQCMKpADwHqN_H7S|s7V^d_=KN1$I~ zw>{h<)7DMi$^iFjPn2yRK6xn#p9lEj%n}bU*buWxoZHavLqFv|2q-S)!IzuVx4>(n zcyg~&U^dKQayiZnMCR0iSe;h%Sq!xvReHoT%54FQ-CwBk}f06iwv%p^gf%68T46x5)T6X2@NvuRm`(cA$)k^Txw7h7v86 z8}n=pQG!9uDr0p->|DzWSothmse+nW=fSaol0*BM9WttP*4H0{8R?oj4q-ie!RTLjAI5c-M>$5HR@ZH%Yu9X1gC( zxoRxPm&of7Ud``P9p+XvFK@9}hj~!STW#<@yShdubGiKEiB)c2l$j_u2`)}@5Y(3M z_1OZG$GFycR$SihHe%?x8X*oHJ$>xXL?YKlU{z6YbWp_QJ_)8r7#coP`>^)rEp|Xu zXKr^8LP@(ufXe)1$v4YVm_rOPgWRRpl4M|9R?{4|QQx4|_p|Z5fBWv(g8r=s)$(me zt=nBvi*<}2MOw_lmI5b{gH=O!eCTwAu88UwC|AI${i3C#9iv;#W27U2ffEo?>A+~G(yIV##-jBt`qI$%BF;J#)~dEWUi||K zB`;CvMoZmPo4G3^!gEQUMHhre3RJQwgE+@!N4+iBEstpi;p7h1JV-|O_nOs;KiR46 zW_0et>@2PA=o4oQ_uwLyL2NhDsj5;aNz!>b;~yLpa&O8uJkg)MEx0AW+*-LluAVJH zf>1y~1kxCti_*+4SD_T&RIZ6T=zuSg=A+beQk=g1l}UDCm-_CCmxYYv6-aa_V8RIw z4g!3)A3*CAE~z(1u#9thBgON#cBf7AVOy6FKh-6?IM zt0(%6`0Ys2ukJbT={rBL;Jm>KWR<^q`ak!W1AyOb$>_yq>Hwl^U_d|!{SXD);&OS> zvl|FbjrrXQu=7YurGhyC=RHF_56nWjv=v~)&y}qI9SC+?EJaIQ2XIhK5)A@E?2LuN zK&if@+kRhM{X0}5_Kia+05t}nch5j=fhy&a(9<=$UzT+LcPiMohiE)WJmFJAfbU`tTc9=i7fa;y%q;Bm04H8R)ZaHu z;RJ}508!u!k^cU_5djM?#!Yj^>q-N-7yx&+0JJ{%H{3S^zZLllY^-{5qQeS6jehg0 zv&z3g0UIwxbfV5Ky$=w50HVklucANtH{v%lFOhY?6rg?hFMvC--vYQ0H0fOi33N~X zR04!_B{7}?Wmz5=v7c>sQ1t&nR>(>ZI93KWL*PHopR+$+8cu=_02BBPwrBhgFkxV` z*~PHFDD2q)0Hy@MfNJSSDE1h#_-7c9^Z#a8qi)STFiQ(yoh@ZmHvi1}mIMEW^PHw2 ze-7YGe#50X`~&V=qFszLN@|4<1#lX_dGe*}KjSX&q@;=1a~Pm&0)96Dcq8m-DE9F5 zx_||4n#E4HLw;icmaAC5r`mME^>3*2;4h{mM4CwrxZRiEVFDZG(N8q=f|Ca|W ztS+{J?2HX+@fQHU&t#t+fq|PqtR@T@Q@}BhK=mp7LlfKtCS6|h$LI^_pMfXL9lj;^ zpRJ%z>P-~Ign;M+w$PrftSu8SZN+ayL0cnJUA;>X%eJll0&GC2s>%+JAN$Nv{)M76H&O*nt* z{U>bt%9X$OpnoypbcprK@cj2o3AXM0xs0EBm! zZ&Uw?vb%A4)VF@a@0-@or`Y*z&p&D57?-D=rr6Ihzg;Q9TTr<%ncS(g+1v0L-J^7*}$Kb7y|T extends */ private boolean lightEnabled; - /** - * Instantiates a new Material editor app state. - * - * @param fileEditor the file editor - */ public BaseMaterialEditor3DState(@NotNull final T fileEditor) { super(fileEditor); this.testBox = new Geometry("Box", new Box(2, 2, 2)); @@ -123,6 +120,7 @@ public BaseMaterialEditor3DState(@NotNull final T fileEditor) { } @Override + @JMEThread protected void registerActionHandlers(@NotNull final ObjectDictionary actionHandlers) { super.registerActionHandlers(actionHandlers); @@ -135,6 +133,7 @@ protected void registerActionHandlers(@NotNull final ObjectDictionary updateMaterialImpl(material)); } @@ -175,6 +178,7 @@ public void updateMaterial(@NotNull final Material material) { * * @param material the new material. */ + @JMEThread protected void updateMaterialImpl(@NotNull final Material material) { final Geometry testBox = getTestBox(); @@ -202,6 +206,7 @@ protected void updateMaterialImpl(@NotNull final Material material) { * * @param modelType the model type */ + @FromAnyThread public void changeMode(@NotNull final ModelType modelType) { EXECUTOR_MANAGER.addJMETask(() -> changeModeImpl(modelType)); } @@ -211,6 +216,7 @@ public void changeMode(@NotNull final ModelType modelType) { * * @param modelType the new model type. */ + @JMEThread protected void changeModeImpl(@NotNull final ModelType modelType) { final Node modelNode = getModelNode(); @@ -239,6 +245,7 @@ protected void changeModeImpl(@NotNull final ModelType modelType) { * * @param bucket the bucket */ + @FromAnyThread public void changeBucketType(@NotNull final Bucket bucket) { EXECUTOR_MANAGER.addJMETask(() -> changeBucketTypeImpl(bucket)); } @@ -248,6 +255,7 @@ public void changeBucketType(@NotNull final Bucket bucket) { * * @param bucket the new bucket. */ + @JMEThread protected void changeBucketTypeImpl(@NotNull final Bucket bucket) { final Geometry testQuad = getTestQuad(); @@ -261,22 +269,26 @@ protected void changeBucketTypeImpl(@NotNull final Bucket bucket) { } @Override + @JMEThread public void initialize(@NotNull final AppStateManager stateManager, @NotNull final Application application) { super.initialize(stateManager, application); changeModeImpl(getCurrentModelType()); } @Override + @JMEThread protected boolean needMovableCamera() { return false; } @Override + @JMEThread protected boolean needEditorCamera() { return true; } @Override + @JMEThread protected boolean needLightForCamera() { return true; } @@ -284,6 +296,7 @@ protected boolean needLightForCamera() { /** * @return the current model mode. */ + @JMEThread protected @NotNull ModelType getCurrentModelType() { return notNull(currentModelType); } @@ -291,6 +304,7 @@ protected boolean needLightForCamera() { /** * @param currentModelType the current model mode. */ + @JMEThread protected void setCurrentModelType(@NotNull final ModelType currentModelType) { this.currentModelType = currentModelType; } @@ -298,6 +312,7 @@ protected void setCurrentModelType(@NotNull final ModelType currentModelType) { /** * @return true if the light is enabled. */ + @JMEThread protected boolean isLightEnabled() { return lightEnabled; } @@ -305,6 +320,7 @@ protected boolean isLightEnabled() { /** * @param lightEnabled true if the light is enabled. */ + @JMEThread protected void setLightEnabled(final boolean lightEnabled) { this.lightEnabled = lightEnabled; } @@ -314,6 +330,7 @@ protected void setLightEnabled(final boolean lightEnabled) { * * @param enabled the enabled */ + @FromAnyThread public void updateLightEnabled(final boolean enabled) { EXECUTOR_MANAGER.addJMETask(() -> updateLightEnabledImpl(enabled)); } @@ -323,6 +340,7 @@ public void updateLightEnabled(final boolean enabled) { * * @param enabled true if light should be enabled. */ + @JMEThread protected void updateLightEnabledImpl(final boolean enabled) { if (enabled == isLightEnabled()) return; @@ -339,6 +357,7 @@ protected void updateLightEnabledImpl(final boolean enabled) { } @Override + @JMEThread protected boolean needUpdateCameraLight() { return false; } diff --git a/src/main/java/com/ss/editor/plugin/api/editor/material/BaseMaterialFileEditor.java b/src/main/java/com/ss/editor/plugin/api/editor/material/BaseMaterialFileEditor.java index 7e17886d..fc09c8b7 100644 --- a/src/main/java/com/ss/editor/plugin/api/editor/material/BaseMaterialFileEditor.java +++ b/src/main/java/com/ss/editor/plugin/api/editor/material/BaseMaterialFileEditor.java @@ -243,6 +243,28 @@ protected boolean needToolbar() { @Override @FXThread protected void createToolbar(@NotNull final HBox container) { + super.createToolbar(container); + createActions(container); + + final Label bucketLabel = new Label(Messages.MATERIAL_FILE_EDITOR_BUCKET_TYPE_LABEL + ":"); + + bucketComboBox = new ComboBox<>(BUCKETS); + bucketComboBox.getSelectionModel().select(Inherit); + bucketComboBox.getSelectionModel() + .selectedItemProperty() + .addListener((observable, oldValue, newValue) -> changeBucketType(newValue)); + + FXUtils.addToPane(bucketLabel, container); + FXUtils.addToPane(bucketComboBox, container); + } + + /** + * Create actions on toolbar. + * + * @param container the container. + */ + @FXThread + protected void createActions(@NotNull final HBox container) { cubeButton = new ToggleButton(); cubeButton.setTooltip(new Tooltip(Messages.MATERIAL_FILE_EDITOR_ACTION_CUBE + " (C)")); @@ -268,24 +290,13 @@ protected void createToolbar(@NotNull final HBox container) { lightButton.setSelected(DEFAULT_LIGHT_ENABLED); lightButton.selectedProperty().addListener((observable, oldValue, newValue) -> changeLight(newValue)); - final Label bucketLabel = new Label(Messages.MATERIAL_FILE_EDITOR_BUCKET_TYPE_LABEL + ":"); - - bucketComboBox = new ComboBox<>(BUCKETS); - bucketComboBox.getSelectionModel().select(Inherit); - bucketComboBox.getSelectionModel() - .selectedItemProperty() - .addListener((observable, oldValue, newValue) -> changeBucketType(newValue)); - FXUtils.addToPane(createSaveAction(), container); FXUtils.addToPane(cubeButton, container); FXUtils.addToPane(sphereButton, container); FXUtils.addToPane(planeButton, container); FXUtils.addToPane(lightButton, container); - FXUtils.addToPane(bucketLabel, container); - FXUtils.addToPane(bucketComboBox, container); DynamicIconSupport.addSupport(cubeButton, sphereButton, planeButton, lightButton); - FXUtils.addClassTo(cubeButton, sphereButton, planeButton, lightButton, CSSClasses.FILE_EDITOR_TOOLBAR_BUTTON); } diff --git a/src/main/java/com/ss/editor/plugin/api/editor/part3d/Advanced3DEditorState.java b/src/main/java/com/ss/editor/plugin/api/editor/part3d/Advanced3DEditorState.java index dce55ad1..53778734 100644 --- a/src/main/java/com/ss/editor/plugin/api/editor/part3d/Advanced3DEditorState.java +++ b/src/main/java/com/ss/editor/plugin/api/editor/part3d/Advanced3DEditorState.java @@ -41,7 +41,7 @@ public Advanced3DEditorState(@NotNull final T fileEditor) { * @return the node on which the camera is looking. */ @FromAnyThread - protected @NotNull Node getCameraNode() { + protected @NotNull Node getCameraNode() { return notNull(cameraNode); } @@ -60,6 +60,7 @@ protected void redo() { } @Override + @JMEThread protected void notifyChangedCameraSettings(@NotNull final Vector3f cameraLocation, final float hRotation, final float vRotation, final float targetDistance, final float cameraSpeed) { super.notifyChangedCameraSettings(cameraLocation, hRotation, vRotation, targetDistance, cameraSpeed); diff --git a/src/main/java/com/ss/editor/plugin/api/editor/part3d/AdvancedPBR3DEditorState.java b/src/main/java/com/ss/editor/plugin/api/editor/part3d/AdvancedPBR3DEditorState.java index a4e27386..d145e39c 100644 --- a/src/main/java/com/ss/editor/plugin/api/editor/part3d/AdvancedPBR3DEditorState.java +++ b/src/main/java/com/ss/editor/plugin/api/editor/part3d/AdvancedPBR3DEditorState.java @@ -5,6 +5,7 @@ import com.jme3.environment.generation.JobProgressAdapter; import com.jme3.light.LightProbe; import com.jme3.scene.Node; +import com.ss.editor.annotation.JMEThread; import com.ss.editor.plugin.api.editor.Advanced3DFileEditor; import org.jetbrains.annotations.NotNull; @@ -42,12 +43,14 @@ public AdvancedPBR3DEditorState(@NotNull final T fileEditor) { } @Override + @JMEThread public void initialize(@NotNull final AppStateManager stateManager, @NotNull final Application application) { super.initialize(stateManager, application); frame = 0; } @Override + @JMEThread public void cleanup() { super.cleanup(); @@ -61,6 +64,7 @@ public void cleanup() { /** * Attach model node to state node. */ + @JMEThread private void attachModelNode() { final Node stateNode = getStateNode(); stateNode.attachChild(modelNode); @@ -69,11 +73,13 @@ private void attachModelNode() { /** * @return the model node. */ + @JMEThread protected @NotNull Node getModelNode() { return modelNode; } @Override + @JMEThread public void update(final float tpf) { super.update(tpf); diff --git a/src/main/java/com/ss/editor/state/editor/impl/AdvancedAbstractEditor3DState.java b/src/main/java/com/ss/editor/state/editor/impl/AdvancedAbstractEditor3DState.java index 43a3d13d..380fd19b 100644 --- a/src/main/java/com/ss/editor/state/editor/impl/AdvancedAbstractEditor3DState.java +++ b/src/main/java/com/ss/editor/state/editor/impl/AdvancedAbstractEditor3DState.java @@ -483,6 +483,7 @@ protected void registerActionHandlers(@NotNull final ObjectDictionary analogHandlers) { analogHandlers.put(MOUSE_X_AXIS, (value, tpf) -> moveXMouse(value)); analogHandlers.put(MOUSE_X_AXIS_NEGATIVE, (value, tpf) -> moveXMouse(-value)); @@ -493,6 +494,7 @@ protected void registerAnalogHandlers(@NotNull final ObjectDictionary getFileEditor().notifyChangedStatus(Status.Stopped)); } @Override + @JMEThread public void update(final float tpf) { super.update(tpf); @@ -193,14 +201,15 @@ public void update(final float tpf) { /** * @return the audio node. */ - @Nullable - private AudioNode getAudioNode() { + @JMEThread + private @Nullable AudioNode getAudioNode() { return audioNode; } /** * @param audioNode the audio node. */ + @JMEThread private void setAudioNode(@Nullable final AudioNode audioNode) { this.audioNode = audioNode; } @@ -208,14 +217,15 @@ private void setAudioNode(@Nullable final AudioNode audioNode) { /** * @return the audio data. */ - @NotNull - private AudioData getAudioData() { + @JMEThread + private @NotNull AudioData getAudioData() { return notNull(audioData); } /** * @param audioData the audio data. */ + @JMEThread private void setAudioData(@NotNull final AudioData audioData) { this.audioData = audioData; } @@ -223,14 +233,15 @@ private void setAudioData(@NotNull final AudioData audioData) { /** * @return the audio key. */ - @NotNull - private AudioKey getAudioKey() { + @JMEThread + private @NotNull AudioKey getAudioKey() { return notNull(audioKey); } /** * @param audioKey the audio key. */ + @JMEThread private void setAudioKey(@NotNull final AudioKey audioKey) { this.audioKey = audioKey; } @@ -240,15 +251,15 @@ private void setAudioKey(@NotNull final AudioKey audioKey) { * * @return the previous status. */ - @Nullable @FromAnyThread - public Status getPrevStatus() { + public @Nullable Status getPrevStatus() { return prevStatus; } /** * @param prevStatus the previous status. */ + @JMEThread private void setPrevStatus(@Nullable final Status prevStatus) { this.prevStatus = prevStatus; } diff --git a/src/main/java/com/ss/editor/state/editor/impl/model/ModelEditor3DState.java b/src/main/java/com/ss/editor/state/editor/impl/model/ModelEditor3DState.java index 7afa3ead..2f20a9da 100644 --- a/src/main/java/com/ss/editor/state/editor/impl/model/ModelEditor3DState.java +++ b/src/main/java/com/ss/editor/state/editor/impl/model/ModelEditor3DState.java @@ -7,14 +7,14 @@ import com.jme3.light.LightProbe; import com.jme3.scene.Node; import com.jme3.scene.Spatial; +import com.ss.editor.annotation.FromAnyThread; +import com.ss.editor.annotation.JMEThread; import com.ss.editor.state.editor.impl.scene.AbstractSceneEditor3DState; import com.ss.editor.ui.component.editor.impl.model.ModelFileEditor; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - import com.ss.rlib.util.array.Array; import com.ss.rlib.util.array.ArrayFactory; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import tonegod.emitter.filter.TonegodTranslucentBucketFilter; /** @@ -62,11 +62,6 @@ public void done(final LightProbe result) { */ private int frame; - /** - * Instantiates a new Model editor app state. - * - * @param fileEditor the file editor - */ public ModelEditor3DState(final ModelFileEditor fileEditor) { super(fileEditor); this.customSkyNode = new Node("Custom Sky"); @@ -81,22 +76,23 @@ public ModelEditor3DState(final ModelFileEditor fileEditor) { /** * @return the node for the placement of custom sky. */ - @NotNull - private Node getCustomSkyNode() { + @JMEThread + private @NotNull Node getCustomSkyNode() { return customSkyNode; } /** * @return the array of custom skies. */ - @NotNull - private Array getCustomSky() { + @JMEThread + private @NotNull Array getCustomSky() { return customSky; } /** * Activate the node with models. */ + @JMEThread private void notifyProbeComplete() { final Node stateNode = getStateNode(); @@ -113,6 +109,7 @@ private void notifyProbeComplete() { /** * @param currentFastSky the current fast sky. */ + @JMEThread private void setCurrentFastSky(@Nullable final Spatial currentFastSky) { this.currentFastSky = currentFastSky; } @@ -120,14 +117,15 @@ private void setCurrentFastSky(@Nullable final Spatial currentFastSky) { /** * @return the current fast sky. */ - @Nullable - private Spatial getCurrentFastSky() { + @JMEThread + private @Nullable Spatial getCurrentFastSky() { return currentFastSky; } /** * @return true if the light of the camera is enabled. */ + @JMEThread private boolean isLightEnabled() { return lightEnabled; } @@ -135,17 +133,20 @@ private boolean isLightEnabled() { /** * @param lightEnabled the flag of activity light of the camera. */ + @JMEThread private void setLightEnabled(final boolean lightEnabled) { this.lightEnabled = lightEnabled; } @Override + @JMEThread public void initialize(@NotNull final AppStateManager stateManager, @NotNull final Application application) { super.initialize(stateManager, application); frame = 0; } @Override + @JMEThread public void cleanup() { super.cleanup(); @@ -155,6 +156,7 @@ public void cleanup() { } @Override + @JMEThread public void update(final float tpf) { super.update(tpf); @@ -172,11 +174,13 @@ public void update(final float tpf) { } @Override + @JMEThread protected boolean needUpdateCameraLight() { return true; } @Override + @JMEThread protected boolean needLightForCamera() { return true; } @@ -186,6 +190,7 @@ protected boolean needLightForCamera() { * * @param enabled the enabled */ + @FromAnyThread public void updateLightEnabled(final boolean enabled) { EXECUTOR_MANAGER.addJMETask(() -> updateLightEnabledImpl(enabled)); } @@ -193,6 +198,7 @@ public void updateLightEnabled(final boolean enabled) { /** * The process of updating the light. */ + @JMEThread private void updateLightEnabledImpl(boolean enabled) { if (enabled == isLightEnabled()) return; @@ -213,6 +219,7 @@ private void updateLightEnabledImpl(boolean enabled) { * * @param fastSky the fast sky */ + @FromAnyThread public void changeFastSky(@Nullable final Spatial fastSky) { EXECUTOR_MANAGER.addJMETask(() -> changeFastSkyImpl(fastSky)); } @@ -220,6 +227,7 @@ public void changeFastSky(@Nullable final Spatial fastSky) { /** * The process of changing the fast sky. */ + @JMEThread private void changeFastSkyImpl(@Nullable final Spatial fastSky) { final Node stateNode = getStateNode(); @@ -246,6 +254,7 @@ private void changeFastSkyImpl(@Nullable final Spatial fastSky) { * * @param sky the sky */ + @FromAnyThread public void addCustomSky(@NotNull final Spatial sky) { EXECUTOR_MANAGER.addJMETask(() -> addCustomSkyImpl(sky)); } @@ -253,6 +262,7 @@ public void addCustomSky(@NotNull final Spatial sky) { /** * The process of adding the custom sky. */ + @JMEThread private void addCustomSkyImpl(@NotNull final Spatial sky) { final Array customSky = getCustomSky(); customSky.add(sky); @@ -263,6 +273,7 @@ private void addCustomSkyImpl(@NotNull final Spatial sky) { * * @param sky the sky */ + @FromAnyThread public void removeCustomSky(@NotNull final Spatial sky) { EXECUTOR_MANAGER.addJMETask(() -> removeCustomSkyImpl(sky)); } @@ -270,6 +281,7 @@ public void removeCustomSky(@NotNull final Spatial sky) { /** * The process of removing the custom sky. */ + @JMEThread private void removeCustomSkyImpl(@NotNull final Spatial sky) { final Array customSky = getCustomSky(); customSky.slowRemove(sky); @@ -278,6 +290,7 @@ private void removeCustomSkyImpl(@NotNull final Spatial sky) { /** * Update the light probe. */ + @FromAnyThread public void updateLightProbe() { EXECUTOR_MANAGER.addJMETask(() -> { diff --git a/src/main/java/com/ss/editor/state/editor/impl/scene/SceneEditor3DState.java b/src/main/java/com/ss/editor/state/editor/impl/scene/SceneEditor3DState.java index f1463f3e..e0899b36 100644 --- a/src/main/java/com/ss/editor/state/editor/impl/scene/SceneEditor3DState.java +++ b/src/main/java/com/ss/editor/state/editor/impl/scene/SceneEditor3DState.java @@ -7,6 +7,7 @@ import com.jme3.post.filters.ToneMapFilter; import com.jme3.scene.Node; import com.ss.editor.annotation.FromAnyThread; +import com.ss.editor.annotation.JMEThread; import com.ss.editor.config.EditorConfig; import com.ss.editor.extension.scene.SceneNode; import com.ss.editor.extension.scene.app.state.SceneAppState; @@ -32,11 +33,6 @@ public class SceneEditor3DState extends AbstractSceneEditor3DState addAppStateImpl(appState)); } + @JMEThread private void addAppStateImpl(@NotNull final SceneAppState appState) { final AppStateManager stateManager = EDITOR.getStateManager(); stateManager.attach(appState); @@ -122,6 +124,7 @@ public void removeAppState(@NotNull final SceneAppState appState) { EXECUTOR_MANAGER.addJMETask(() -> removeAppStateImpl(appState)); } + @JMEThread private void removeAppStateImpl(@NotNull final SceneAppState appState) { final AppStateManager stateManager = EDITOR.getStateManager(); stateManager.detach(appState); @@ -137,6 +140,7 @@ public void addFilter(@NotNull final SceneFilter sceneFilter) { EXECUTOR_MANAGER.addJMETask(() -> addFilterImpl(sceneFilter)); } + @JMEThread private void addFilterImpl(@NotNull final SceneFilter sceneFilter) { final FilterPostProcessor postProcessor = EDITOR.getPostProcessor(); postProcessor.addFilter(sceneFilter.get()); @@ -152,6 +156,7 @@ public void removeFilter(@NotNull final SceneFilter sceneFilter) { EXECUTOR_MANAGER.addJMETask(() -> removeFilterImpl(sceneFilter)); } + @JMEThread private void removeFilterImpl(@NotNull final SceneFilter sceneFilter) { final FilterPostProcessor postProcessor = EDITOR.getPostProcessor(); postProcessor.removeFilter(sceneFilter.get()); @@ -160,6 +165,7 @@ private void removeFilterImpl(@NotNull final SceneFilter sceneFilter) { /** * @return true if need to show light models. */ + @JMEThread private boolean isLightShowed() { return lightShowed; } @@ -167,6 +173,7 @@ private boolean isLightShowed() { /** * @param lightShowed true if need to show light models. */ + @JMEThread private void setLightShowed(final boolean lightShowed) { this.lightShowed = lightShowed; } @@ -174,6 +181,7 @@ private void setLightShowed(final boolean lightShowed) { /** * @return true if need to show audio models. */ + @JMEThread private boolean isAudioShowed() { return audioShowed; } @@ -181,6 +189,7 @@ private boolean isAudioShowed() { /** * @param audioShowed true if need to show audio models. */ + @JMEThread private void setAudioShowed(final boolean audioShowed) { this.audioShowed = audioShowed; } @@ -190,6 +199,7 @@ private void setAudioShowed(final boolean audioShowed) { * * @param showed the showed */ + @FromAnyThread public void updateLightShowed(final boolean showed) { EXECUTOR_MANAGER.addJMETask(() -> updateLightShowedImpl(showed)); } @@ -197,6 +207,7 @@ public void updateLightShowed(final boolean showed) { /** * The process to change light showing. */ + @JMEThread private void updateLightShowedImpl(final boolean showed) { if (showed == isLightShowed()) return; @@ -217,6 +228,7 @@ private void updateLightShowedImpl(final boolean showed) { * * @param showed the showed */ + @FromAnyThread public void updateAudioShowed(final boolean showed) { EXECUTOR_MANAGER.addJMETask(() -> updateAudioShowedImpl(showed)); } @@ -224,6 +236,7 @@ public void updateAudioShowed(final boolean showed) { /** * The process to change audio showing. */ + @JMEThread private void updateAudioShowedImpl(final boolean showed) { if (showed == isAudioShowed()) return; From 47e5fcc06417b9571167b10427099c58e167282d Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Thu, 14 Sep 2017 06:55:32 +0300 Subject: [PATCH 16/50] added import icon. --- src/main/java/com/ss/editor/ui/Icons.java | 4 ++ src/main/resources/credits/icons.txt | 3 +- src/main/resources/ui/icons/svg/import.svg | 44 ++++++++++++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 src/main/resources/ui/icons/svg/import.svg diff --git a/src/main/java/com/ss/editor/ui/Icons.java b/src/main/java/com/ss/editor/ui/Icons.java index 0d8ee850..2aa64ad6 100644 --- a/src/main/java/com/ss/editor/ui/Icons.java +++ b/src/main/java/com/ss/editor/ui/Icons.java @@ -130,6 +130,10 @@ public interface Icons { * The constant EXPORT_16. */ Image EXPORT_16 = ICON_MANAGER.getImage("/ui/icons/svg/scale-symbol.svg", 16); + /** + * The constant IMPORT_16. + */ + Image IMPORT_16 = ICON_MANAGER.getImage("/ui/icons/svg/import.svg", 16); /** * The constant EXPORT_16. */ diff --git a/src/main/resources/credits/icons.txt b/src/main/resources/credits/icons.txt index b4ce9f99..4eb5f496 100644 --- a/src/main/resources/credits/icons.txt +++ b/src/main/resources/credits/icons.txt @@ -80,4 +80,5 @@ ui/icons/svg/scale-symbol.svg icon made by https://www.flaticon.com/authors/popc ui/icons/svg/font-selection-editor.svg made by https://www.flaticon.com/authors/dave-gandy from www.flaticon.com ui/icons/svg/inbox.svg icon made by http://www.flaticon.com/authors/freepik from www.flaticon.com ui/icons/svg/debug.svg icon made by http://www.flaticon.com/authors/freepik from www.flaticon.com -ui/icons/filetypes/vector.svg icon made by https://www.flaticon.com/authors/freepik from www.flaticon.com \ No newline at end of file +ui/icons/filetypes/vector.svg icon made by https://www.flaticon.com/authors/freepik from www.flaticon.com +ui/icons/svg/import.svg icon made by https://www.flaticon.com/authors/anatoly from www.flaticon.com \ No newline at end of file diff --git a/src/main/resources/ui/icons/svg/import.svg b/src/main/resources/ui/icons/svg/import.svg new file mode 100644 index 00000000..a702b113 --- /dev/null +++ b/src/main/resources/ui/icons/svg/import.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From aa368e04bd80a9613380425c8fbfbd5f3c364f81 Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Thu, 14 Sep 2017 09:16:31 +0300 Subject: [PATCH 17/50] fixed a bug with asset dialog, updated property controls API. --- .../ui/control/property/PropertyControl.java | 16 ++ .../property/impl/BooleanPropertyControl.java | 11 + .../property/impl/ColorPropertyControl.java | 10 + .../property/impl/FloatPropertyControl.java | 10 + .../property/impl/IntegerPropertyControl.java | 10 + .../impl/Texture2DPropertyControl.java | 5 + .../impl/Vector2FPropertyControl.java | 44 +++- .../Vector3FSingleRowPropertyControl.java | 237 ++++++++++++++++++ .../dialog/asset/file/AssetEditorDialog.java | 2 +- .../java/com/ss/editor/ui/util/UIUtils.java | 17 +- 10 files changed, 350 insertions(+), 12 deletions(-) create mode 100644 src/main/java/com/ss/editor/ui/control/property/impl/Vector3FSingleRowPropertyControl.java diff --git a/src/main/java/com/ss/editor/ui/control/property/PropertyControl.java b/src/main/java/com/ss/editor/ui/control/property/PropertyControl.java index a739ff9d..175a82c8 100644 --- a/src/main/java/com/ss/editor/ui/control/property/PropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/property/PropertyControl.java @@ -332,6 +332,22 @@ protected void createComponents() { FXUtils.addToPane(container, this); } + /** + * Change control width percent. + * + * @param controlWidthPercent the control width percent. + */ + @FXThread + public void changeControlWidthPercent(final double controlWidthPercent) { + + if (!isSingleRow()) { + return; + } + + propertyNameLabel.maxWidthProperty().unbind(); + propertyNameLabel.maxWidthProperty().bind(widthProperty().multiply(1D - controlWidthPercent)); + } + /** * Is single row boolean. * diff --git a/src/main/java/com/ss/editor/ui/control/property/impl/BooleanPropertyControl.java b/src/main/java/com/ss/editor/ui/control/property/impl/BooleanPropertyControl.java index 4c16e5ce..6f1215fc 100644 --- a/src/main/java/com/ss/editor/ui/control/property/impl/BooleanPropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/property/impl/BooleanPropertyControl.java @@ -10,6 +10,7 @@ import com.ss.rlib.ui.util.FXUtils; import javafx.scene.control.CheckBox; import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -54,6 +55,16 @@ protected void createComponents(@NotNull final HBox container) { FXUtils.addClassTo(checkBox, CSSClasses.ABSTRACT_PARAM_CONTROL_CHECK_BOX); } + /** + * Disable the offset of checkbox control. + */ + @FXThread + public void disableCheckboxOffset() { + final CheckBox checkBox = getCheckBox(); + checkBox.prefWidthProperty().unbind(); + checkBox.setPrefWidth(Region.USE_COMPUTED_SIZE); + } + @Override @FromAnyThread protected boolean isSingleRow() { diff --git a/src/main/java/com/ss/editor/ui/control/property/impl/ColorPropertyControl.java b/src/main/java/com/ss/editor/ui/control/property/impl/ColorPropertyControl.java index e3b2a4dc..7c7a29cb 100644 --- a/src/main/java/com/ss/editor/ui/control/property/impl/ColorPropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/property/impl/ColorPropertyControl.java @@ -47,6 +47,16 @@ protected void createComponents(@NotNull final HBox container) { FXUtils.addClassTo(colorPicker, CSSClasses.ABSTRACT_PARAM_CONTROL_COLOR_PICKER); } + @Override + @FXThread + public void changeControlWidthPercent(final double controlWidthPercent) { + super.changeControlWidthPercent(controlWidthPercent); + + final ColorPicker colorPicker = getColorPicker(); + colorPicker.prefWidthProperty().unbind(); + colorPicker.prefWidthProperty().bind(widthProperty().multiply(controlWidthPercent)); + } + @Override @FXThread protected void setPropertyValue(@Nullable final ColorRGBA color) { diff --git a/src/main/java/com/ss/editor/ui/control/property/impl/FloatPropertyControl.java b/src/main/java/com/ss/editor/ui/control/property/impl/FloatPropertyControl.java index b06f0aba..078825d5 100644 --- a/src/main/java/com/ss/editor/ui/control/property/impl/FloatPropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/property/impl/FloatPropertyControl.java @@ -41,6 +41,16 @@ public FloatPropertyControl(@Nullable final Float propertyValue, @NotNull final super(propertyValue, propertyName, changeConsumer, changeHandler); } + @Override + @FXThread + public void changeControlWidthPercent(final double controlWidthPercent) { + super.changeControlWidthPercent(controlWidthPercent); + + final FloatTextField valueField = getValueField(); + valueField.prefWidthProperty().unbind(); + valueField.prefWidthProperty().bind(widthProperty().multiply(controlWidthPercent)); + } + @Override @FXThread protected void createComponents(@NotNull final HBox container) { diff --git a/src/main/java/com/ss/editor/ui/control/property/impl/IntegerPropertyControl.java b/src/main/java/com/ss/editor/ui/control/property/impl/IntegerPropertyControl.java index 0cffc73c..f021b75a 100644 --- a/src/main/java/com/ss/editor/ui/control/property/impl/IntegerPropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/property/impl/IntegerPropertyControl.java @@ -32,6 +32,16 @@ public IntegerPropertyControl(@Nullable final Integer propertyValue, @NotNull fi super(propertyValue, propertyName, changeConsumer); } + @Override + @FXThread + public void changeControlWidthPercent(final double controlWidthPercent) { + super.changeControlWidthPercent(controlWidthPercent); + + final IntegerTextField valueField = getValueField(); + valueField.prefWidthProperty().unbind(); + valueField.prefWidthProperty().bind(widthProperty().multiply(controlWidthPercent)); + } + @Override @FXThread protected void createComponents(@NotNull final HBox container) { diff --git a/src/main/java/com/ss/editor/ui/control/property/impl/Texture2DPropertyControl.java b/src/main/java/com/ss/editor/ui/control/property/impl/Texture2DPropertyControl.java index 84ebfe95..bb47f64c 100644 --- a/src/main/java/com/ss/editor/ui/control/property/impl/Texture2DPropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/property/impl/Texture2DPropertyControl.java @@ -97,6 +97,11 @@ public Texture2DPropertyControl(@Nullable final Texture2D propertyValue, @NotNul setOnDragDropped(this::handleDragDroppedEvent); } + @Override + @FXThread + public void changeControlWidthPercent(final double controlWidthPercent) { + } + /** * Handle drag dropped events. * diff --git a/src/main/java/com/ss/editor/ui/control/property/impl/Vector2FPropertyControl.java b/src/main/java/com/ss/editor/ui/control/property/impl/Vector2FPropertyControl.java index 9e6e4332..f2a88bf8 100644 --- a/src/main/java/com/ss/editor/ui/control/property/impl/Vector2FPropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/property/impl/Vector2FPropertyControl.java @@ -37,42 +37,66 @@ public class Vector2FPropertyControl extends Proper @Nullable private FloatTextField yField; + /** + * The field container. + */ + @Nullable + private HBox fieldContainer; + public Vector2FPropertyControl(@Nullable final Vector2f propertyValue, @NotNull final String propertyName, @NotNull final C changeConsumer) { super(propertyValue, propertyName, changeConsumer); } + @Override + @FXThread + public void changeControlWidthPercent(final double controlWidthPercent) { + super.changeControlWidthPercent(controlWidthPercent); + + final HBox valueField = getFieldContainer(); + valueField.prefWidthProperty().unbind(); + valueField.prefWidthProperty().bind(widthProperty().multiply(controlWidthPercent)); + } + @Override @FXThread protected void createComponents(@NotNull final HBox container) { super.createComponents(container); - final HBox field = new HBox(); - field.prefWidthProperty() + fieldContainer = new HBox(); + fieldContainer.prefWidthProperty() .bind(widthProperty().multiply(CONTROL_WIDTH_PERCENT)); xField = new FloatTextField(); xField.setOnKeyReleased(this::updateVector); xField.addChangeListener((observable, oldValue, newValue) -> updateVector(null)); - xField.prefWidthProperty().bind(field.widthProperty().multiply(0.5)); + xField.prefWidthProperty().bind(fieldContainer.widthProperty().multiply(0.5)); xField.setScrollPower(10F); yField = new FloatTextField(); yField.setOnKeyReleased(this::updateVector); yField.addChangeListener((observable, oldValue, newValue) -> updateVector(null)); - yField.prefWidthProperty().bind(field.widthProperty().multiply(0.5)); + yField.prefWidthProperty().bind(fieldContainer.widthProperty().multiply(0.5)); yField.setScrollPower(10F); - FXUtils.addToPane(xField, field); - FXUtils.addToPane(yField, field); - FXUtils.addToPane(field, container); + FXUtils.addToPane(xField, fieldContainer); + FXUtils.addToPane(yField, fieldContainer); + FXUtils.addToPane(fieldContainer, container); - FXUtils.addClassesTo(field, CSSClasses.DEF_HBOX, CSSClasses.TEXT_INPUT_CONTAINER, + FXUtils.addClassesTo(fieldContainer, CSSClasses.DEF_HBOX, CSSClasses.TEXT_INPUT_CONTAINER, CSSClasses.ABSTRACT_PARAM_CONTROL_SHORT_INPUT_CONTAINER); FXUtils.addClassesTo(xField, yField, CSSClasses.TRANSPARENT_TEXT_FIELD); - UIUtils.addFocusBinding(field, xField, yField); + UIUtils.addFocusBinding(fieldContainer, xField, yField); + } + + /** + * @return the field container. + */ + @FXThread + private @NotNull HBox getFieldContainer() { + return notNull(fieldContainer); } @Override @@ -143,7 +167,7 @@ protected void reload() { final FloatTextField yField = getYField(); yField.setValue(vector.getY()); - yField.positionCaret(xField.getText().length()); + yField.positionCaret(yField.getText().length()); } /** diff --git a/src/main/java/com/ss/editor/ui/control/property/impl/Vector3FSingleRowPropertyControl.java b/src/main/java/com/ss/editor/ui/control/property/impl/Vector3FSingleRowPropertyControl.java new file mode 100644 index 00000000..299cc0cd --- /dev/null +++ b/src/main/java/com/ss/editor/ui/control/property/impl/Vector3FSingleRowPropertyControl.java @@ -0,0 +1,237 @@ +package com.ss.editor.ui.control.property.impl; + +import static com.ss.rlib.util.ObjectUtils.notNull; +import com.jme3.math.Vector3f; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; +import com.ss.editor.model.undo.editor.ChangeConsumer; +import com.ss.editor.ui.control.property.PropertyControl; +import com.ss.editor.ui.css.CSSClasses; +import com.ss.editor.ui.util.UIUtils; +import com.ss.rlib.ui.control.input.FloatTextField; +import com.ss.rlib.ui.util.FXUtils; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; +import javafx.scene.layout.HBox; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * The implementation of the {@link PropertyControl} to edit {@link com.jme3.math.Vector3f} values. + * + * @param the type parameter + * @param the type parameter + * @author JavaSaBr. + */ +public class Vector3FSingleRowPropertyControl extends PropertyControl { + + /** + * The field X. + */ + @Nullable + private FloatTextField xField; + + /** + * The field Y. + */ + @Nullable + private FloatTextField yField; + + /** + * The field Z. + */ + @Nullable + private FloatTextField zField; + + /** + * The field container. + */ + @Nullable + private HBox fieldContainer; + + public Vector3FSingleRowPropertyControl(@Nullable final Vector3f propertyValue, @NotNull final String propertyName, + @NotNull final C changeConsumer) { + super(propertyValue, propertyName, changeConsumer); + } + + @Override + @FXThread + public void changeControlWidthPercent(final double controlWidthPercent) { + super.changeControlWidthPercent(controlWidthPercent); + + final HBox valueField = getFieldContainer(); + valueField.prefWidthProperty().unbind(); + valueField.prefWidthProperty().bind(widthProperty().multiply(controlWidthPercent)); + } + + @Override + @FXThread + protected void createComponents(@NotNull final HBox container) { + super.createComponents(container); + + fieldContainer = new HBox(); + fieldContainer.prefWidthProperty().bind(widthProperty().multiply(CONTROL_WIDTH_PERCENT)); + + xField = new FloatTextField(); + xField.setOnKeyReleased(this::updateVector); + xField.addChangeListener((observable, oldValue, newValue) -> updateVector(null)); + xField.prefWidthProperty().bind(fieldContainer.widthProperty().multiply(0.33)); + xField.setScrollPower(10F); + + yField = new FloatTextField(); + yField.setOnKeyReleased(this::updateVector); + yField.addChangeListener((observable, oldValue, newValue) -> updateVector(null)); + yField.prefWidthProperty().bind(fieldContainer.widthProperty().multiply(0.33)); + yField.setScrollPower(10F); + + zField = new FloatTextField(); + zField.setOnKeyReleased(this::updateVector); + zField.addChangeListener((observable, oldValue, newValue) -> updateVector(null)); + zField.prefWidthProperty().bind(fieldContainer.widthProperty().multiply(0.33)); + zField.setScrollPower(10F); + + FXUtils.addToPane(xField, fieldContainer); + FXUtils.addToPane(yField, fieldContainer); + FXUtils.addToPane(zField, fieldContainer); + FXUtils.addToPane(fieldContainer, container); + + FXUtils.addClassesTo(fieldContainer, CSSClasses.DEF_HBOX, CSSClasses.TEXT_INPUT_CONTAINER, + CSSClasses.ABSTRACT_PARAM_CONTROL_SHORT_INPUT_CONTAINER); + FXUtils.addClassesTo(xField, yField, zField, CSSClasses.TRANSPARENT_TEXT_FIELD); + + UIUtils.addFocusBinding(fieldContainer, xField, yField, zField); + } + + /** + * @return the field container. + */ + @FXThread + private @NotNull HBox getFieldContainer() { + return notNull(fieldContainer); + } + + @Override + @FXThread + protected void setPropertyValue(@Nullable final Vector3f vector) { + super.setPropertyValue(vector == null ? null : vector.clone()); + } + + @Override + @FromAnyThread + protected boolean isSingleRow() { + return true; + } + + /** + * Check result x value. + * + * @param x the x. + * @param y the y. + * @param z the z. + * @return the result x value. + */ + @FXThread + protected float checkResultXValue(final float x, final float y, final float z) { + return x; + } + + /** + * Check result y value. + * + * @param x the x. + * @param y the y. + * @param z the z. + * @return the result y value. + */ + @FXThread + protected float checkResultYValue(final float x, final float y, final float z) { + return y; + } + + /** + * Check result z value. + * + * @param x the x. + * @param y the y. + * @param z the z. + * @return the result z value. + */ + @FXThread + protected float checkResultZValue(final float x, final float y, final float z) { + return z; + } + + /** + * Get the X field. + * + * @return the X field. + */ + @FXThread + protected @NotNull FloatTextField getXField() { + return notNull(xField); + } + + /** + * Get the Y field. + * + * @return the Y field. + */ + @FXThread + protected @NotNull FloatTextField getYField() { + return notNull(yField); + } + + /** + * Get the Z field. + * + * @return the Z field. + */ + @FXThread + protected @NotNull FloatTextField getZField() { + return notNull(zField); + } + + @Override + @FXThread + protected void reload() { + + final Vector3f vector = getPropertyValue() == null ? Vector3f.ZERO : getPropertyValue(); + + final FloatTextField xField = getXField(); + xField.setValue(vector.getX()); + xField.positionCaret(xField.getText().length()); + + final FloatTextField yField = getYField(); + yField.setValue(vector.getY()); + yField.positionCaret(yField.getText().length()); + + final FloatTextField zField = getZField(); + zField.setValue(vector.getZ()); + zField.positionCaret(zField.getText().length()); + } + + /** + * Update the current value. + * + * @param event the change event. + */ + @FXThread + private void updateVector(@Nullable final KeyEvent event) { + if (isIgnoreListener() || (event != null && event.getCode() != KeyCode.ENTER)) return; + + final FloatTextField xField = getXField(); + final float x = xField.getValue(); + + final FloatTextField yField = getYField(); + final float y = yField.getValue(); + + final FloatTextField zField = getZField(); + final float z = zField.getValue(); + + final Vector3f oldValue = getPropertyValue() == null ? Vector3f.ZERO : getPropertyValue(); + final Vector3f newValue = new Vector3f(); + newValue.set(checkResultXValue(x, y, z), checkResultYValue(x, y, z), checkResultZValue(x, y, z)); + + changed(newValue, oldValue.clone()); + } +} diff --git a/src/main/java/com/ss/editor/ui/dialog/asset/file/AssetEditorDialog.java b/src/main/java/com/ss/editor/ui/dialog/asset/file/AssetEditorDialog.java index c71196ea..ffbde3c9 100644 --- a/src/main/java/com/ss/editor/ui/dialog/asset/file/AssetEditorDialog.java +++ b/src/main/java/com/ss/editor/ui/dialog/asset/file/AssetEditorDialog.java @@ -142,7 +142,7 @@ public void setOnlyFolders(final boolean onlyFolders) { */ @FXThread protected void processOpen(@NotNull final ResourceElement element) { - processOk(); + hide(); } @Override diff --git a/src/main/java/com/ss/editor/ui/util/UIUtils.java b/src/main/java/com/ss/editor/ui/util/UIUtils.java index 9f708bcb..cf37e2fe 100644 --- a/src/main/java/com/ss/editor/ui/util/UIUtils.java +++ b/src/main/java/com/ss/editor/ui/util/UIUtils.java @@ -45,6 +45,7 @@ import java.util.Set; import java.util.function.BiConsumer; import java.util.function.Consumer; +import java.util.function.Function; import java.util.function.Predicate; /** @@ -590,8 +591,22 @@ public static void updateEditedCell(final Labeled cell) { @FXThread public static void openResourceAssetDialog(@NotNull final Consumer handler, @NotNull final Array resources) { + openResourceAssetDialog(handler, null, resources); + } + + /** + * Open an resource asset dialog. + * + * @param handler the result handler. + * @param validator the validator. + * @param resources the resources. + */ + @FXThread + public static void openResourceAssetDialog(@NotNull final Consumer handler, + @Nullable final Function validator, + @NotNull final Array resources) { - final StringVirtualAssetEditorDialog dialog = new StringVirtualAssetEditorDialog(handler, resources); + final StringVirtualAssetEditorDialog dialog = new StringVirtualAssetEditorDialog(handler, validator, resources); dialog.show(); } From 20c1e70d94c12e2c5f8c06bda8001bab6b991137 Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Thu, 14 Sep 2017 11:29:52 +0300 Subject: [PATCH 18/50] updated property controls API. --- build.gradle | 2 +- .../ui/control/property/PropertyControl.java | 11 ++ .../property/impl/BooleanPropertyControl.java | 20 +++ .../impl/FloatArrayPropertyControl.java | 134 ++++++++++++++++++ .../impl/IntArrayPropertyControl.java | 10 ++ .../impl/Texture2DPropertyControl.java | 66 ++++++--- .../Texture2DSingleRowPropertyControl.java | 28 ++++ .../java/com/ss/editor/ui/css/CSSClasses.java | 5 + src/main/resources/ui/css/custom_classes.css | 23 +++ 9 files changed, 281 insertions(+), 18 deletions(-) create mode 100644 src/main/java/com/ss/editor/ui/control/property/impl/FloatArrayPropertyControl.java create mode 100644 src/main/java/com/ss/editor/ui/control/property/impl/Texture2DSingleRowPropertyControl.java diff --git a/build.gradle b/build.gradle index c748fd0e..943110ce 100644 --- a/build.gradle +++ b/build.gradle @@ -59,7 +59,7 @@ dependencies { compile 'org.controlsfx:controlsfx:8.40.13' compile 'com.github.JavaSaBr:RlibFX:4.1.3' - compile 'com.github.JavaSaBr:RLib:6.4.0' + compile 'com.github.JavaSaBr:RLib:6.4.1' compile 'com.github.JavaSaBr:JME3-JFX:1.6.1' // https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 diff --git a/src/main/java/com/ss/editor/ui/control/property/PropertyControl.java b/src/main/java/com/ss/editor/ui/control/property/PropertyControl.java index 175a82c8..f42eea22 100644 --- a/src/main/java/com/ss/editor/ui/control/property/PropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/property/PropertyControl.java @@ -332,6 +332,16 @@ protected void createComponents() { FXUtils.addToPane(container, this); } + /** + * Get the property name label. + * + * @return the property name label. + */ + @FXThread + protected @NotNull Label getPropertyNameLabel() { + return propertyNameLabel; + } + /** * Change control width percent. * @@ -344,6 +354,7 @@ public void changeControlWidthPercent(final double controlWidthPercent) { return; } + final Label propertyNameLabel = getPropertyNameLabel(); propertyNameLabel.maxWidthProperty().unbind(); propertyNameLabel.maxWidthProperty().bind(widthProperty().multiply(1D - controlWidthPercent)); } diff --git a/src/main/java/com/ss/editor/ui/control/property/impl/BooleanPropertyControl.java b/src/main/java/com/ss/editor/ui/control/property/impl/BooleanPropertyControl.java index 6f1215fc..075be8ed 100644 --- a/src/main/java/com/ss/editor/ui/control/property/impl/BooleanPropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/property/impl/BooleanPropertyControl.java @@ -8,7 +8,9 @@ import com.ss.editor.ui.css.CSSClasses; import com.ss.rlib.function.SixObjectConsumer; import com.ss.rlib.ui.util.FXUtils; +import javafx.beans.property.DoubleProperty; import javafx.scene.control.CheckBox; +import javafx.scene.control.Label; import javafx.scene.layout.HBox; import javafx.scene.layout.Region; import org.jetbrains.annotations.NotNull; @@ -55,14 +57,32 @@ protected void createComponents(@NotNull final HBox container) { FXUtils.addClassTo(checkBox, CSSClasses.ABSTRACT_PARAM_CONTROL_CHECK_BOX); } + @Override + @FXThread + public void changeControlWidthPercent(final double controlWidthPercent) { + + final CheckBox checkBox = getCheckBox(); + final DoubleProperty widthProperty = checkBox.prefWidthProperty(); + + if (widthProperty.isBound()) { + super.changeControlWidthPercent(controlWidthPercent); + widthProperty.unbind(); + widthProperty.bind(widthProperty().multiply(controlWidthPercent)); + } + } + /** * Disable the offset of checkbox control. */ @FXThread public void disableCheckboxOffset() { + final CheckBox checkBox = getCheckBox(); checkBox.prefWidthProperty().unbind(); checkBox.setPrefWidth(Region.USE_COMPUTED_SIZE); + + final Label propertyNameLabel = getPropertyNameLabel(); + propertyNameLabel.maxWidthProperty().unbind(); } @Override diff --git a/src/main/java/com/ss/editor/ui/control/property/impl/FloatArrayPropertyControl.java b/src/main/java/com/ss/editor/ui/control/property/impl/FloatArrayPropertyControl.java new file mode 100644 index 00000000..e67d9c63 --- /dev/null +++ b/src/main/java/com/ss/editor/ui/control/property/impl/FloatArrayPropertyControl.java @@ -0,0 +1,134 @@ +package com.ss.editor.ui.control.property.impl; + +import static com.ss.rlib.util.ObjectUtils.notNull; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; +import com.ss.editor.model.undo.editor.ChangeConsumer; +import com.ss.editor.ui.control.property.PropertyControl; +import com.ss.editor.ui.css.CSSClasses; +import com.ss.rlib.function.SixObjectConsumer; +import com.ss.rlib.ui.util.FXUtils; +import com.ss.rlib.util.ArrayUtils; +import com.ss.rlib.util.StringUtils; +import javafx.scene.control.TextField; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; +import javafx.scene.layout.HBox; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.function.BiConsumer; + +/** + * The implementation of the {@link PropertyControl} to edit float array values. + * + * @param the type parameter + * @param the type parameter + * @author JavaSaBr + */ +public class FloatArrayPropertyControl extends PropertyControl { + + /** + * The filed with current value. + */ + @Nullable + private TextField valueField; + + public FloatArrayPropertyControl(@Nullable final float[] propertyValue, @NotNull final String propertyName, + @NotNull final C changeConsumer) { + super(propertyValue, propertyName, changeConsumer); + } + + public FloatArrayPropertyControl(@Nullable final float[] propertyValue, @NotNull final String propertyName, + @NotNull final C changeConsumer, + @Nullable final SixObjectConsumer> changeHandler) { + super(propertyValue, propertyName, changeConsumer, changeHandler); + } + + @Override + @FXThread + public void changeControlWidthPercent(final double controlWidthPercent) { + super.changeControlWidthPercent(controlWidthPercent); + + final TextField valueField = getValueField(); + valueField.prefWidthProperty().unbind(); + valueField.prefWidthProperty().bind(widthProperty().multiply(controlWidthPercent)); + } + + @Override + @FXThread + protected void createComponents(@NotNull final HBox container) { + super.createComponents(container); + + valueField = new TextField(); + valueField.setOnKeyReleased(this::updateValue); + valueField.prefWidthProperty() + .bind(widthProperty().multiply(CONTROL_WIDTH_PERCENT)); + + FXUtils.addClassTo(valueField, CSSClasses.ABSTRACT_PARAM_CONTROL_COMBO_BOX); + FXUtils.addToPane(valueField, container); + } + + @Override + @FromAnyThread + protected boolean isSingleRow() { + return true; + } + + /** + * @return the filed with current value. + */ + @FXThread + private @NotNull TextField getValueField() { + return notNull(valueField); + } + + @Override + @FXThread + protected void reload() { + + final float[] element = getPropertyValue(); + + final TextField valueField = getValueField(); + final int caretPosition = valueField.getCaretPosition(); + + if (element == null) { + valueField.setText(StringUtils.EMPTY); + } else { + valueField.setText(ArrayUtils.toString(element, " ", false, false)); + } + + valueField.positionCaret(caretPosition); + } + + /** + * Update the value. + */ + @FXThread + private void updateValue(@Nullable final KeyEvent event) { + if (isIgnoreListener() || (event != null && event.getCode() != KeyCode.ENTER)) return; + + final String textValue = getValueField().getText(); + float[] newValue = null; + + if (!StringUtils.isEmpty(textValue)) { + + final String splitter = textValue.contains(" ") ? " " : ","; + final String[] splited = textValue.split(splitter); + + newValue = new float[splited.length]; + + for (int i = 0; i < splited.length; i++) { + try { + newValue[i] = Float.parseFloat(splited[i]); + } catch (final NumberFormatException e) { + LOGGER.warning(this, e); + newValue = getPropertyValue(); + break; + } + } + } + + changed(newValue, getPropertyValue()); + } +} diff --git a/src/main/java/com/ss/editor/ui/control/property/impl/IntArrayPropertyControl.java b/src/main/java/com/ss/editor/ui/control/property/impl/IntArrayPropertyControl.java index 155ea12b..e8379c14 100644 --- a/src/main/java/com/ss/editor/ui/control/property/impl/IntArrayPropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/property/impl/IntArrayPropertyControl.java @@ -45,6 +45,16 @@ public IntArrayPropertyControl(@Nullable final int[] propertyValue, @NotNull fin super(propertyValue, propertyName, changeConsumer, changeHandler); } + @Override + @FXThread + public void changeControlWidthPercent(final double controlWidthPercent) { + super.changeControlWidthPercent(controlWidthPercent); + + final TextField valueField = getValueField(); + valueField.prefWidthProperty().unbind(); + valueField.prefWidthProperty().bind(widthProperty().multiply(controlWidthPercent)); + } + @Override @FXThread protected void createComponents(@NotNull final HBox container) { diff --git a/src/main/java/com/ss/editor/ui/control/property/impl/Texture2DPropertyControl.java b/src/main/java/com/ss/editor/ui/control/property/impl/Texture2DPropertyControl.java index bb47f64c..e0f402c9 100644 --- a/src/main/java/com/ss/editor/ui/control/property/impl/Texture2DPropertyControl.java +++ b/src/main/java/com/ss/editor/ui/control/property/impl/Texture2DPropertyControl.java @@ -90,6 +90,12 @@ public class Texture2DPropertyControl extends Prope @Nullable private Label textureLabel; + /** + * The field container. + */ + @Nullable + private HBox fieldContainer; + public Texture2DPropertyControl(@Nullable final Texture2D propertyValue, @NotNull final String propertyName, @NotNull final C changeConsumer) { super(propertyValue, propertyName, changeConsumer); @@ -127,7 +133,12 @@ protected void handleDragOverEvent(@NotNull final DragEvent dragEvent) { protected void createComponents(@NotNull final HBox container) { super.createComponents(container); - textureLabel = new Label(NO_TEXTURE); + fieldContainer = new HBox(); + + if (!isSingleRow()) { + fieldContainer.prefWidthProperty().bind(container.widthProperty()); + } + textureTooltip = new ImageChannelPreview(); final VBox previewContainer = new VBox(); @@ -152,23 +163,32 @@ protected void createComponents(@NotNull final HBox container) { removeButton.setOnAction(event -> processRemove()); removeButton.disableProperty().bind(buildDisableRemoveCondition()); - textureLabel.prefWidthProperty().bind(widthProperty() - .subtract(removeButton.widthProperty()) - .subtract(previewContainer.widthProperty()) - .subtract(settingsButton.widthProperty()) - .subtract(addButton.widthProperty())); - - FXUtils.addToPane(textureLabel, container); - FXUtils.addToPane(previewContainer, container); - FXUtils.addToPane(addButton, container); - FXUtils.addToPane(settingsButton, container); - FXUtils.addToPane(removeButton, container); + if (!isSingleRow()) { + + textureLabel = new Label(NO_TEXTURE); + textureLabel.prefWidthProperty().bind(widthProperty() + .subtract(removeButton.widthProperty()) + .subtract(previewContainer.widthProperty()) + .subtract(settingsButton.widthProperty()) + .subtract(addButton.widthProperty())); + + FXUtils.addToPane(textureLabel, fieldContainer); + FXUtils.addClassTo(textureLabel, CSSClasses.ABSTRACT_PARAM_CONTROL_ELEMENT_LABEL); + FXUtils.addClassesTo(fieldContainer, CSSClasses.TEXT_INPUT_CONTAINER, + CSSClasses.ABSTRACT_PARAM_CONTROL_INPUT_CONTAINER); + + } else { + FXUtils.addClassesTo(fieldContainer, CSSClasses.TEXT_INPUT_CONTAINER_WITHOUT_PADDING); + } + + FXUtils.addToPane(previewContainer, fieldContainer); + FXUtils.addToPane(addButton, fieldContainer); + FXUtils.addToPane(settingsButton, fieldContainer); + FXUtils.addToPane(removeButton, fieldContainer); + FXUtils.addToPane(fieldContainer, container); FXUtils.addToPane(texturePreview, previewContainer); - FXUtils.addClassesTo(container, CSSClasses.DEF_HBOX, CSSClasses.TEXT_INPUT_CONTAINER, - CSSClasses.ABSTRACT_PARAM_CONTROL_INPUT_CONTAINER); FXUtils.addClassTo(previewContainer, CSSClasses.ABSTRACT_PARAM_CONTROL_PREVIEW_CONTAINER); - FXUtils.addClassTo(textureLabel, CSSClasses.ABSTRACT_PARAM_CONTROL_ELEMENT_LABEL); FXUtils.addClassesTo(settingsButton, addButton, removeButton, CSSClasses.FLAT_BUTTON, CSSClasses.INPUT_CONTROL_TOOLBAR_BUTTON); } @@ -189,6 +209,16 @@ protected void createComponents(@NotNull final HBox container) { return notNull(textureLabel); } + /** + * Get the field container. + * + * @return the field container. + */ + @FXThread + protected @NotNull HBox getFieldContainer() { + return notNull(fieldContainer); + } + /** * @return the texture preview. */ @@ -325,8 +355,10 @@ protected void reload() { final Texture2D texture2D = getPropertyValue(); final AssetKey key = texture2D == null ? null : texture2D.getKey(); - final Label textureLabel = getTextureLabel(); - textureLabel.setText(key == null ? NO_TEXTURE : key.getName()); + if (!isSingleRow()) { + final Label textureLabel = getTextureLabel(); + textureLabel.setText(key == null ? NO_TEXTURE : key.getName()); + } final ImageChannelPreview textureTooltip = getTextureTooltip(); final ImageView preview = getTexturePreview(); diff --git a/src/main/java/com/ss/editor/ui/control/property/impl/Texture2DSingleRowPropertyControl.java b/src/main/java/com/ss/editor/ui/control/property/impl/Texture2DSingleRowPropertyControl.java new file mode 100644 index 00000000..97ce2c58 --- /dev/null +++ b/src/main/java/com/ss/editor/ui/control/property/impl/Texture2DSingleRowPropertyControl.java @@ -0,0 +1,28 @@ +package com.ss.editor.ui.control.property.impl; + +import com.jme3.texture.Texture2D; +import com.ss.editor.annotation.FromAnyThread; +import com.ss.editor.model.undo.editor.ChangeConsumer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * The single row implementation of the control to edit textures. + * + * @param the type of a {@link ChangeConsumer} + * @param the type of an editing object. + * @author JavaSaBr + */ +public class Texture2DSingleRowPropertyControl extends Texture2DPropertyControl { + + public Texture2DSingleRowPropertyControl(@Nullable final Texture2D propertyValue, + @NotNull final String propertyName, @NotNull final C changeConsumer) { + super(propertyValue, propertyName, changeConsumer); + } + + @Override + @FromAnyThread + protected boolean isSingleRow() { + return true; + } +} diff --git a/src/main/java/com/ss/editor/ui/css/CSSClasses.java b/src/main/java/com/ss/editor/ui/css/CSSClasses.java index d143b38f..31d1ba78 100644 --- a/src/main/java/com/ss/editor/ui/css/CSSClasses.java +++ b/src/main/java/com/ss/editor/ui/css/CSSClasses.java @@ -79,6 +79,11 @@ public interface CSSClasses { */ String TEXT_INPUT_CONTAINER = "text-input-container"; + /** + * The constant TEXT_INPUT_CONTAINER_WITHOUT_PADDING. + */ + String TEXT_INPUT_CONTAINER_WITHOUT_PADDING = "text-input-container-without-padding"; + /** * The constant CHOOSE_TEXTURE_CONTROL. */ diff --git a/src/main/resources/ui/css/custom_classes.css b/src/main/resources/ui/css/custom_classes.css index a461f3f3..d74aeffd 100644 --- a/src/main/resources/ui/css/custom_classes.css +++ b/src/main/resources/ui/css/custom_classes.css @@ -147,6 +147,29 @@ -fx-opacity: -var-disabled-opacity; } +.text-input-container-without-padding { + -fx-background: -var-button-background-color; + -fx-background-color: -fx-background; + -fx-background-insets: 0px; + -fx-background-radius: 2px; + -fx-border-color: -var-border-color; + -fx-border-width: 1px; + -fx-border-radius: 1px; + -fx-padding: 1px 1px 1px 1px; + -fx-min-height: -var-default-field-height; +} + +.text-input-container-without-padding:focused { + -fx-border-color: -var-border-color, -fx-focus-color; + -fx-border-width: 1px, 1px; + -fx-border-insets: 0 0 0 0, 1 1 1 1; + -fx-padding: 0px 0px 0px 0px; +} + +.text-input-container-without-padding:disabled { + -fx-opacity: -var-disabled-opacity; +} + /******************************************************************************* * * * Transparent Text Area * From bc70d482d9c0a7810123b1ccbfd3d2919ef5ce53 Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Fri, 15 Sep 2017 06:46:09 +0300 Subject: [PATCH 19/50] separated code area components. --- .../editor/impl/CodeAreaFileEditor.java | 32 ++-- .../component/editor/impl/GLSLFileEditor.java | 106 +------------ .../impl/MaterialDefinitionFileEditor.java | 115 +------------- .../editor/ui/control/code/BaseCodeArea.java | 47 ++++++ .../editor/ui/control/code/GLSLCodeArea.java | 116 ++++++++++++++ .../code/MaterialDefinitionCodeArea.java | 141 ++++++++++++++++++ 6 files changed, 325 insertions(+), 232 deletions(-) create mode 100644 src/main/java/com/ss/editor/ui/control/code/BaseCodeArea.java create mode 100644 src/main/java/com/ss/editor/ui/control/code/GLSLCodeArea.java create mode 100644 src/main/java/com/ss/editor/ui/control/code/MaterialDefinitionCodeArea.java diff --git a/src/main/java/com/ss/editor/ui/component/editor/impl/CodeAreaFileEditor.java b/src/main/java/com/ss/editor/ui/component/editor/impl/CodeAreaFileEditor.java index 58a40cf9..6e271bd8 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/impl/CodeAreaFileEditor.java +++ b/src/main/java/com/ss/editor/ui/component/editor/impl/CodeAreaFileEditor.java @@ -3,14 +3,13 @@ import static com.ss.rlib.util.ObjectUtils.notNull; import com.ss.editor.annotation.BackgroundThread; import com.ss.editor.annotation.FXThread; +import com.ss.editor.ui.control.code.BaseCodeArea; import com.ss.editor.ui.css.CSSClasses; import com.ss.rlib.ui.util.FXUtils; import com.ss.rlib.util.FileUtils; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import org.fxmisc.richtext.CodeArea; -import org.fxmisc.richtext.model.StyleSpans; -import org.fxmisc.undo.UndoManager; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -18,7 +17,6 @@ import java.io.PrintWriter; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Collection; /** * The implementation of editor to edit files with code. @@ -37,7 +35,7 @@ public abstract class CodeAreaFileEditor extends AbstractFileEditor { * The code area. */ @Nullable - private CodeArea codeArea; + private BaseCodeArea codeArea; @Override @FXThread @@ -49,9 +47,7 @@ public abstract class CodeAreaFileEditor extends AbstractFileEditor { @FXThread protected void createContent(@NotNull final VBox root) { - codeArea = new CodeArea(); - codeArea.richChanges().filter(ch -> !ch.getInserted().equals(ch.getRemoved())) - .subscribe(change -> codeArea.setStyleSpans(0, getStyleSpans(codeArea.getText()))); + codeArea = createCodeArea(); codeArea.textProperty().addListener((observable, oldValue, newValue) -> updateDirty(newValue)); codeArea.prefHeightProperty().bind(root.heightProperty()); codeArea.prefWidthProperty().bind(root.widthProperty()); @@ -61,14 +57,13 @@ protected void createContent(@NotNull final VBox root) { } /** - * Gets style spans. + * Create the code area. * - * @param text the text - * @return the style spans + * @return the code area. */ @FXThread - protected @NotNull StyleSpans> getStyleSpans(@NotNull final String text) { - throw new RuntimeException("unsupported"); + protected @NotNull BaseCodeArea createCodeArea() { + throw new RuntimeException(); } /** @@ -96,7 +91,7 @@ protected void createToolbar(@NotNull final HBox container) { * @return the code area. */ @FXThread - private @NotNull CodeArea getCodeArea() { + private @NotNull BaseCodeArea getCodeArea() { return notNull(codeArea); } @@ -107,11 +102,8 @@ public void openFile(@NotNull final Path file) { setOriginalContent(FileUtils.read(file)); - final CodeArea codeArea = getCodeArea(); - codeArea.appendText(getOriginalContent()); - - final UndoManager undoManager = codeArea.getUndoManager(); - undoManager.forgetHistory(); + final BaseCodeArea codeArea = getCodeArea(); + codeArea.loadContent(getOriginalContent()); setOriginalContent(codeArea.getText()); updateDirty(getOriginalContent()); @@ -165,9 +157,9 @@ protected void handleExternalChanges() { final String newContent = FileUtils.read(getEditFile()); - final CodeArea codeArea = getCodeArea(); + final BaseCodeArea codeArea = getCodeArea(); final String currentContent = codeArea.getText(); - codeArea.replaceText(0, currentContent.length(), newContent); + codeArea.reloadContent(newContent); setOriginalContent(currentContent); updateDirty(newContent); diff --git a/src/main/java/com/ss/editor/ui/component/editor/impl/GLSLFileEditor.java b/src/main/java/com/ss/editor/ui/component/editor/impl/GLSLFileEditor.java index 5c9e788d..2f616435 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/impl/GLSLFileEditor.java +++ b/src/main/java/com/ss/editor/ui/component/editor/impl/GLSLFileEditor.java @@ -1,19 +1,14 @@ package com.ss.editor.ui.component.editor.impl; -import static java.util.Collections.singleton; import com.ss.editor.FileExtensions; import com.ss.editor.Messages; import com.ss.editor.annotation.FXThread; import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.component.editor.EditorDescription; -import org.fxmisc.richtext.model.StyleSpans; -import org.fxmisc.richtext.model.StyleSpansBuilder; +import com.ss.editor.ui.control.code.BaseCodeArea; +import com.ss.editor.ui.control.code.GLSLCodeArea; import org.jetbrains.annotations.NotNull; -import java.util.Collection; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - /** * The implementation of editor to edit GLSL files. * @@ -34,101 +29,10 @@ public class GLSLFileEditor extends CodeAreaFileEditor { DESCRIPTION.setExtensions(FileExtensions.SHADER_EXTENSIONS); } - @NotNull - private static final String[] KEYWORDS = { - "define", "undef", "if", "ifdef", "ifndef", - "else", "elif", "endif", "error", "pragma", - "extension", "version", "line", "attribute", "const", - "uniform", "varying", "layout", "centroid", "flat", - "smooth", "noperspective", "patch", "sample", "break", - "continue", "do", "for", "while", "switch", - "case", "default", "if", "subroutine", "in", "out", "inout", - "void", "true", "false", "invariant", "discard", "return", "struct" - }; - - @NotNull - private static final String[] VALUE_TYPES = { - "float", "double", "int", "bool", "mat2", "mat3", "mat4", "uint", "uvec2", "uvec3", "uvec4", - "sampler1D", "sampler2D", "sampler3D", "samplerCube", "vec2", "vec3", "vec4" - }; - - private static final String KEYWORD_PATTERN = "\\b(" + String.join("|", KEYWORDS) + ")\\b"; - private static final String VALUE_TYPE_PATTERN = "\\b(" + String.join("|", VALUE_TYPES) + ")\\b"; - private static final String PAREN_PATTERN = "\\(|\\)"; - private static final String BRACE_PATTERN = "\\{|\\}"; - private static final String BRACKET_PATTERN = "\\[|\\]"; - private static final String SEMICOLON_PATTERN = "\\;"; - private static final String STRING_PATTERN = "\"([^\"\\\\]|\\\\.)*\""; - private static final String COMMENT_PATTERN = "//[^\n]*" + "|" + "/\\*(.|\\R)*?\\*/"; - - private static final Pattern PATTERN = Pattern.compile( - "(?" + KEYWORD_PATTERN + ")" - + "|(?" + VALUE_TYPE_PATTERN + ")" - + "|(?" + PAREN_PATTERN + ")" - + "|(?" + BRACE_PATTERN + ")" - + "|(?" + BRACKET_PATTERN + ")" - + "|(?" + SEMICOLON_PATTERN + ")" - + "|(?" + STRING_PATTERN + ")" - + "|(?" + COMMENT_PATTERN + ")" - ); - - @NotNull - private static StyleSpans> computeHighlighting(@NotNull final String text) { - - final Matcher matcher = PATTERN.matcher(text); - final StyleSpansBuilder> spansBuilder = new StyleSpansBuilder<>(); - - int lastKwEnd = 0; - - while (matcher.find()) { - - String styleClass = matcher.group("KEYWORD") != null ? "keyword" : null; - - if (styleClass == null) { - styleClass = matcher.group("VALUETYPE") != null ? "value-type" : null; - } - - if (styleClass == null) { - styleClass = matcher.group("PAREN") != null ? "paren" : null; - } - - if (styleClass == null) { - styleClass = matcher.group("BRACE") != null ? "brace" : null; - } - - if (styleClass == null) { - styleClass = matcher.group("BRACKET") != null ? "bracket" : null; - } - - if (styleClass == null) { - styleClass = matcher.group("SEMICOLON") != null ? "semicolon" : null; - } - - if (styleClass == null) { - styleClass = matcher.group("STRING") != null ? "string" : null; - } - - if (styleClass == null) { - styleClass = matcher.group("COMMENT") != null ? "comment" : null; - } - - assert styleClass != null; - - spansBuilder.add(singleton("plain-code"), matcher.start() - lastKwEnd); - spansBuilder.add(singleton(styleClass), matcher.end() - matcher.start()); - - lastKwEnd = matcher.end(); - } - - spansBuilder.add(singleton("plain-code"), text.length() - lastKwEnd); - - return spansBuilder.create(); - } - - @FXThread @Override - protected @NotNull StyleSpans> getStyleSpans(@NotNull final String text) { - return computeHighlighting(text); + @FXThread + protected @NotNull BaseCodeArea createCodeArea() { + return new GLSLCodeArea(); } @Override diff --git a/src/main/java/com/ss/editor/ui/component/editor/impl/MaterialDefinitionFileEditor.java b/src/main/java/com/ss/editor/ui/component/editor/impl/MaterialDefinitionFileEditor.java index 034eb1de..596e0c90 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/impl/MaterialDefinitionFileEditor.java +++ b/src/main/java/com/ss/editor/ui/component/editor/impl/MaterialDefinitionFileEditor.java @@ -1,19 +1,14 @@ package com.ss.editor.ui.component.editor.impl; -import static java.util.Collections.singleton; import com.ss.editor.FileExtensions; import com.ss.editor.Messages; import com.ss.editor.annotation.FXThread; import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.component.editor.EditorDescription; -import org.fxmisc.richtext.model.StyleSpans; -import org.fxmisc.richtext.model.StyleSpansBuilder; +import com.ss.editor.ui.control.code.BaseCodeArea; +import com.ss.editor.ui.control.code.MaterialDefinitionCodeArea; import org.jetbrains.annotations.NotNull; -import java.util.Collection; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - /** * The implementation of editor to edit material definition files. * @@ -34,112 +29,10 @@ public class MaterialDefinitionFileEditor extends CodeAreaFileEditor { DESCRIPTION.addExtension(FileExtensions.JME_MATERIAL_DEFINITION); } - @NotNull - private static final String[] KEYWORDS = { - "MaterialDef", "MaterialParameters", "Technique", "WorldParameters", "Defines", "ForcedRenderState" - }; - - @NotNull - private static final String[] VALUE_TYPES = { - "Texture2D", "Float", "Boolean", "Int", "Color", "Vector3", "TextureCubeMap", "Matrix4", "Vector4", "Vector2", - "VertexShader", "TessellationEvaluationShader ", "TessellationControlShader", "FragmentShader", "LightMode", - "WorldViewProjectionMatrix", "Time", "NormalMatrix", "WorldViewMatrix", - "ViewMatrix", "CameraPosition", "WorldMatrix", "FaceCull", "DepthTest", "DepthWrite", "PolyOffset", - "ColorWrite", "Blend", "Resolution", "FragmentShader", "ViewProjectionMatrix", - "IntArray", "FloatArray", "Vector2Array", "Vector3Array", "Vector4Array", "Matrix3", - "Matrix3Array", "Matrix4Array", "TextureBuffer", "Texture3D", "TextureArray", "GeometryShader" - }; - - @NotNull - private static final String[] VALUE_VALUES = { - "true", "false", "Off", "On", "True", "False", "Disable", "SinglePass", "MultiPass", - "SinglePassAndImageBased", "FixedPipeline", "StaticPass", "InPass", "PostPass", "World", "View", - "Legacy", "GLSL100", "GLSL110", "GLSL120", "GLSL130", "GLSL140", "GLSL150", "GLSL400", "GLSL330", - "GLSL410", "GLSL420", "GLSL430", "GLSL440", "GLSL450", - }; - - private static final String KEYWORD_PATTERN = "\\b(" + String.join("|", KEYWORDS) + ")\\b"; - private static final String VALUE_TYPE_PATTERN = "\\b(" + String.join("|", VALUE_TYPES) + ")\\b"; - private static final String VALUE_VALUE_PATTERN = "\\b(" + String.join("|", VALUE_VALUES) + ")\\b"; - private static final String PAREN_PATTERN = "\\(|\\)"; - private static final String BRACE_PATTERN = "\\{|\\}"; - private static final String BRACKET_PATTERN = "\\[|\\]"; - private static final String SEMICOLON_PATTERN = "\\;"; - private static final String STRING_PATTERN = "\"([^\"\\\\]|\\\\.)*\""; - private static final String COMMENT_PATTERN = "//[^\n]*" + "|" + "/\\*(.|\\R)*?\\*/"; - - private static final Pattern PATTERN = Pattern.compile( - "(?" + KEYWORD_PATTERN + ")" - + "|(?" + VALUE_TYPE_PATTERN + ")" - + "|(?" + VALUE_VALUE_PATTERN + ")" - + "|(?" + PAREN_PATTERN + ")" - + "|(?" + BRACE_PATTERN + ")" - + "|(?" + BRACKET_PATTERN + ")" - + "|(?" + SEMICOLON_PATTERN + ")" - + "|(?" + STRING_PATTERN + ")" - + "|(?" + COMMENT_PATTERN + ")" - ); - - private static StyleSpans> computeHighlighting(final String text) { - - final Matcher matcher = PATTERN.matcher(text); - final StyleSpansBuilder> spansBuilder = new StyleSpansBuilder<>(); - - int lastKwEnd = 0; - - while (matcher.find()) { - - String styleClass = matcher.group("KEYWORD") != null ? "keyword" : null; - - if (styleClass == null) { - styleClass = matcher.group("VALUETYPE") != null ? "value-type" : null; - } - - if (styleClass == null) { - styleClass = matcher.group("VALUEVALUE") != null ? "value-value" : null; - } - - if (styleClass == null) { - styleClass = matcher.group("PAREN") != null ? "paren" : null; - } - - if (styleClass == null) { - styleClass = matcher.group("BRACE") != null ? "brace" : null; - } - - if (styleClass == null) { - styleClass = matcher.group("BRACKET") != null ? "bracket" : null; - } - - if (styleClass == null) { - styleClass = matcher.group("SEMICOLON") != null ? "semicolon" : null; - } - - if (styleClass == null) { - styleClass = matcher.group("STRING") != null ? "string" : null; - } - - if (styleClass == null) { - styleClass = matcher.group("COMMENT") != null ? "comment" : null; - } - - assert styleClass != null; - - spansBuilder.add(singleton("plain-code"), matcher.start() - lastKwEnd); - spansBuilder.add(singleton(styleClass), matcher.end() - matcher.start()); - - lastKwEnd = matcher.end(); - } - - spansBuilder.add(singleton("plain-code"), text.length() - lastKwEnd); - - return spansBuilder.create(); - } - @Override @FXThread - protected @NotNull StyleSpans> getStyleSpans(@NotNull final String text) { - return computeHighlighting(text); + protected @NotNull BaseCodeArea createCodeArea() { + return new MaterialDefinitionCodeArea(); } @Override diff --git a/src/main/java/com/ss/editor/ui/control/code/BaseCodeArea.java b/src/main/java/com/ss/editor/ui/control/code/BaseCodeArea.java new file mode 100644 index 00000000..d2a28539 --- /dev/null +++ b/src/main/java/com/ss/editor/ui/control/code/BaseCodeArea.java @@ -0,0 +1,47 @@ +package com.ss.editor.ui.control.code; + +import com.ss.editor.annotation.FXThread; +import org.fxmisc.richtext.CodeArea; +import org.fxmisc.richtext.model.StyleSpans; +import org.fxmisc.undo.UndoManager; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; + +/** + * The base implementation of code area control for this editor. + * + * @author JavaSaBr + */ +public class BaseCodeArea extends CodeArea { + + public BaseCodeArea() { + richChanges().filter(ch -> !ch.getInserted().equals(ch.getRemoved())) + .subscribe(change -> setStyleSpans(0, calculateStyleSpans(getText()))); + } + + /** + * Gets style spans. + * + * @param text the text + * @return the style spans + */ + @FXThread + protected @NotNull StyleSpans> calculateStyleSpans(@NotNull final String text) { + throw new RuntimeException("unsupported"); + } + + @FXThread + public void loadContent(@NotNull final String content) { + appendText(content); + + final UndoManager undoManager = getUndoManager(); + undoManager.forgetHistory(); + } + + @FXThread + public void reloadContent(@NotNull final String content) { + final String currentContent = getText(); + replaceText(0, currentContent.length(), content); + } +} diff --git a/src/main/java/com/ss/editor/ui/control/code/GLSLCodeArea.java b/src/main/java/com/ss/editor/ui/control/code/GLSLCodeArea.java new file mode 100644 index 00000000..a64c564e --- /dev/null +++ b/src/main/java/com/ss/editor/ui/control/code/GLSLCodeArea.java @@ -0,0 +1,116 @@ +package com.ss.editor.ui.control.code; + +import static java.util.Collections.singleton; +import com.ss.editor.annotation.FXThread; +import org.fxmisc.richtext.model.StyleSpans; +import org.fxmisc.richtext.model.StyleSpansBuilder; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * The implementation of code area to show glsl code. + * + * @author JavaSaBr + */ +public class GLSLCodeArea extends BaseCodeArea { + + @NotNull + private static final String[] KEYWORDS = { + "define", "undef", "if", "ifdef", "ifndef", + "else", "elif", "endif", "error", "pragma", + "extension", "version", "line", "attribute", "const", + "uniform", "varying", "layout", "centroid", "flat", + "smooth", "noperspective", "patch", "sample", "break", + "continue", "do", "for", "while", "switch", + "case", "default", "if", "subroutine", "in", "out", "inout", + "void", "true", "false", "invariant", "discard", "return", "struct" + }; + + @NotNull + private static final String[] VALUE_TYPES = { + "float", "double", "int", "bool", "mat2", "mat3", "mat4", "uint", "uvec2", "uvec3", "uvec4", + "sampler1D", "sampler2D", "sampler3D", "samplerCube", "vec2", "vec3", "vec4" + }; + + private static final String KEYWORD_PATTERN = "\\b(" + String.join("|", KEYWORDS) + ")\\b"; + private static final String VALUE_TYPE_PATTERN = "\\b(" + String.join("|", VALUE_TYPES) + ")\\b"; + private static final String PAREN_PATTERN = "\\(|\\)"; + private static final String BRACE_PATTERN = "\\{|\\}"; + private static final String BRACKET_PATTERN = "\\[|\\]"; + private static final String SEMICOLON_PATTERN = "\\;"; + private static final String STRING_PATTERN = "\"([^\"\\\\]|\\\\.)*\""; + private static final String COMMENT_PATTERN = "//[^\n]*" + "|" + "/\\*(.|\\R)*?\\*/"; + + private static final Pattern PATTERN = Pattern.compile( + "(?" + KEYWORD_PATTERN + ")" + + "|(?" + VALUE_TYPE_PATTERN + ")" + + "|(?" + PAREN_PATTERN + ")" + + "|(?" + BRACE_PATTERN + ")" + + "|(?" + BRACKET_PATTERN + ")" + + "|(?" + SEMICOLON_PATTERN + ")" + + "|(?" + STRING_PATTERN + ")" + + "|(?" + COMMENT_PATTERN + ")" + ); + + @FXThread + private static @NotNull StyleSpans> computeHighlighting(@NotNull final String text) { + + final Matcher matcher = PATTERN.matcher(text); + final StyleSpansBuilder> spansBuilder = new StyleSpansBuilder<>(); + + int lastKwEnd = 0; + + while (matcher.find()) { + + String styleClass = matcher.group("KEYWORD") != null ? "keyword" : null; + + if (styleClass == null) { + styleClass = matcher.group("VALUETYPE") != null ? "value-type" : null; + } + + if (styleClass == null) { + styleClass = matcher.group("PAREN") != null ? "paren" : null; + } + + if (styleClass == null) { + styleClass = matcher.group("BRACE") != null ? "brace" : null; + } + + if (styleClass == null) { + styleClass = matcher.group("BRACKET") != null ? "bracket" : null; + } + + if (styleClass == null) { + styleClass = matcher.group("SEMICOLON") != null ? "semicolon" : null; + } + + if (styleClass == null) { + styleClass = matcher.group("STRING") != null ? "string" : null; + } + + if (styleClass == null) { + styleClass = matcher.group("COMMENT") != null ? "comment" : null; + } + + assert styleClass != null; + + spansBuilder.add(singleton("plain-code"), matcher.start() - lastKwEnd); + spansBuilder.add(singleton(styleClass), matcher.end() - matcher.start()); + + lastKwEnd = matcher.end(); + } + + spansBuilder.add(singleton("plain-code"), text.length() - lastKwEnd); + + return spansBuilder.create(); + } + + @Override + @FXThread + protected @NotNull StyleSpans> calculateStyleSpans(@NotNull final String text) { + return computeHighlighting(text); + } +} diff --git a/src/main/java/com/ss/editor/ui/control/code/MaterialDefinitionCodeArea.java b/src/main/java/com/ss/editor/ui/control/code/MaterialDefinitionCodeArea.java new file mode 100644 index 00000000..4f7e20a8 --- /dev/null +++ b/src/main/java/com/ss/editor/ui/control/code/MaterialDefinitionCodeArea.java @@ -0,0 +1,141 @@ +package com.ss.editor.ui.control.code; + +import static java.util.Collections.singleton; +import com.ss.editor.annotation.FXThread; +import org.fxmisc.richtext.model.StyleSpans; +import org.fxmisc.richtext.model.StyleSpansBuilder; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * The implementation of code area to show glsl code. + * + * @author JavaSaBr + */ +public class MaterialDefinitionCodeArea extends BaseCodeArea { + + @NotNull + private static final String[] KEYWORDS = { + "MaterialDef", "MaterialParameters", "Technique", "WorldParameters", "Defines", "ForcedRenderState", + "VertexShaderNodes", "ShaderNode", "Definition", "InputMappings", "OutputMappings", "FragmentShaderNodes" + }; + + @NotNull + private static final String[] VALUE_TYPES = { + "Texture2D", "Float", "Boolean", "Int", "Color", "Vector3", "TextureCubeMap", "Matrix4", "Vector4", "Vector2", + "VertexShader", "TessellationEvaluationShader ", "TessellationControlShader", "FragmentShader", "LightMode", + "FaceCull", "DepthTest", "DepthWrite", "PolyOffset", + "ColorWrite", "Blend", "FragmentShader", "IntArray", "FloatArray", "Vector2Array", "Vector3Array", + "Vector4Array", "Matrix3", "Matrix3Array", "Matrix4Array", "TextureBuffer", "Texture3D", "TextureArray", + "GeometryShader", "Global", "WorldParam", "Attr" + }; + + @NotNull + private static final String[] VALUE_VALUES = { + "true", "false", "Off", "On", "True", "False", "Disable", "SinglePass", "MultiPass", + "SinglePassAndImageBased", "FixedPipeline", "StaticPass", "InPass", "PostPass", "World", "View", + "Legacy", + // glsl versions + "GLSL100", "GLSL110", "GLSL120", "GLSL130", "GLSL140", "GLSL150", "GLSL400", "GLSL330", + "GLSL410", "GLSL420", "GLSL430", "GLSL440", "GLSL450", + // attributes + "Tangent", "Binormal", "InterleavedData", "Index", "BindPosePosition", "BindPoseNormal", + "BoneWeight", "BoneIndex", "BindPoseTangent", "HWBoneWeight", "HWBoneIndex", "InstanceData", + "Position", "Size", "Normal", "TexCoord", "Color", + // uniforms + "WorldViewProjectionMatrix", "Time", "NormalMatrix", "WorldViewMatrix", "ViewMatrix", "CameraPosition", + "WorldMatrix", "Resolution", "ViewProjectionMatrix", "ProjectionMatrix", "NormalMatrix", "WorldMatrixInverseTranspose", + "WorldMatrixInverse", "ViewMatrixInverse", "ProjectionMatrixInverse", "ViewProjectionMatrixInverse", + "WorldViewMatrixInverse", "NormalMatrixInverse", "WorldViewProjectionMatrixInverse", + "ViewPort", "FrustumNearFar", "ResolutionInverse", "Aspect", "CameraDirection", "CameraLeft", "CameraUp", + "Tpf", "FrameRate", "LightDirection", "LightPosition", "AmbientLightColor", "LightColor" + }; + + private static final String KEYWORD_PATTERN = "\\b(" + String.join("|", KEYWORDS) + ")\\b"; + private static final String VALUE_TYPE_PATTERN = "\\b(" + String.join("|", VALUE_TYPES) + ")\\b"; + private static final String VALUE_VALUE_PATTERN = "\\b(" + String.join("|", VALUE_VALUES) + ")\\b"; + private static final String PAREN_PATTERN = "\\(|\\)"; + private static final String BRACE_PATTERN = "\\{|\\}"; + private static final String BRACKET_PATTERN = "\\[|\\]"; + private static final String SEMICOLON_PATTERN = "\\;"; + private static final String STRING_PATTERN = "\"([^\"\\\\]|\\\\.)*\""; + private static final String COMMENT_PATTERN = "//[^\n]*" + "|" + "/\\*(.|\\R)*?\\*/"; + + private static final Pattern PATTERN = Pattern.compile( + "(?" + KEYWORD_PATTERN + ")" + + "|(?" + VALUE_TYPE_PATTERN + ")" + + "|(?" + VALUE_VALUE_PATTERN + ")" + + "|(?" + PAREN_PATTERN + ")" + + "|(?" + BRACE_PATTERN + ")" + + "|(?" + BRACKET_PATTERN + ")" + + "|(?" + SEMICOLON_PATTERN + ")" + + "|(?" + STRING_PATTERN + ")" + + "|(?" + COMMENT_PATTERN + ")" + ); + + @FXThread + private static @NotNull StyleSpans> computeHighlighting(final String text) { + + final Matcher matcher = PATTERN.matcher(text); + final StyleSpansBuilder> spansBuilder = new StyleSpansBuilder<>(); + + int lastKwEnd = 0; + + while (matcher.find()) { + + String styleClass = matcher.group("KEYWORD") != null ? "keyword" : null; + + if (styleClass == null) { + styleClass = matcher.group("VALUETYPE") != null ? "value-type" : null; + } + + if (styleClass == null) { + styleClass = matcher.group("VALUEVALUE") != null ? "value-value" : null; + } + + if (styleClass == null) { + styleClass = matcher.group("PAREN") != null ? "paren" : null; + } + + if (styleClass == null) { + styleClass = matcher.group("BRACE") != null ? "brace" : null; + } + + if (styleClass == null) { + styleClass = matcher.group("BRACKET") != null ? "bracket" : null; + } + + if (styleClass == null) { + styleClass = matcher.group("SEMICOLON") != null ? "semicolon" : null; + } + + if (styleClass == null) { + styleClass = matcher.group("STRING") != null ? "string" : null; + } + + if (styleClass == null) { + styleClass = matcher.group("COMMENT") != null ? "comment" : null; + } + + assert styleClass != null; + + spansBuilder.add(singleton("plain-code"), matcher.start() - lastKwEnd); + spansBuilder.add(singleton(styleClass), matcher.end() - matcher.start()); + + lastKwEnd = matcher.end(); + } + + spansBuilder.add(singleton("plain-code"), text.length() - lastKwEnd); + + return spansBuilder.create(); + } + + @Override + @FXThread + protected @NotNull StyleSpans> calculateStyleSpans(@NotNull final String text) { + return computeHighlighting(text); + } +} From c06ae748fe17820cdcb973916deeeec512832b54 Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Fri, 15 Sep 2017 16:43:33 +0300 Subject: [PATCH 20/50] refactoring. --- src/main/java/com/ss/editor/Editor.java | 85 +- src/main/java/com/ss/editor/EditorThread.java | 5 +- .../java/com/ss/editor/FileExtensions.java | 173 +- .../com/ss/editor/FolderAssetLocator.java | 4 + .../java/com/ss/editor/JFXApplication.java | 20 +- src/main/java/com/ss/editor/Messages.java | 1754 +---------------- src/main/java/com/ss/editor/UpdateBSS.java | 28 - src/main/java/com/ss/editor/ui/Icons.java | 471 +---- .../MaterialSettingsTreeNodeFactory.java | 2 + .../ui/control/tree/node/TreeNodeFactory.java | 6 +- .../node/impl/AnimationTreeNodeFactory.java | 6 +- .../node/impl/CollisionTreeNodeFactory.java | 5 +- .../node/impl/ControlTreeNodeFactory.java | 5 +- .../impl/DefaultParticlesTreeNodeFactory.java | 6 +- .../node/impl/DefaultTreeNodeFactory.java | 5 +- .../tree/node/impl/LightTreeNodeFactory.java | 5 +- .../node/impl/PrimitiveTreeNodeFactory.java | 6 +- .../node/impl/Toneg0dTreeNodeFactory.java | 6 +- .../java/com/ss/editor/ui/css/CSSClasses.java | 662 ++----- .../java/com/ss/editor/ui/css/CSSIds.java | 64 +- .../com/ss/editor/ui/css/CSSRegistry.java | 8 +- .../com/ss/editor/ui/css/CssColorTheme.java | 14 +- .../com/ss/editor/ui/scene/EditorFXScene.java | 23 +- .../ss/editor/ui/tooltip/CustomTooltip.java | 20 +- .../ui/tooltip/ImageChannelPreview.java | 11 + .../ss/editor/ui/tooltip/ImagePreview.java | 4 + .../ss/editor/ui/util/DynamicIconSupport.java | 8 + .../java/com/ss/editor/ui/util/UIUtils.java | 10 + 28 files changed, 495 insertions(+), 2921 deletions(-) delete mode 100644 src/main/java/com/ss/editor/UpdateBSS.java diff --git a/src/main/java/com/ss/editor/Editor.java b/src/main/java/com/ss/editor/Editor.java index 56583bd6..5c682e51 100644 --- a/src/main/java/com/ss/editor/Editor.java +++ b/src/main/java/com/ss/editor/Editor.java @@ -30,6 +30,8 @@ import com.jme3.system.AppSettings; import com.jme3x.jfx.injfx.JmeToJFXApplication; import com.ss.editor.analytics.google.GAnalytics; +import com.ss.editor.annotation.FromAnyThread; +import com.ss.editor.annotation.JMEThread; import com.ss.editor.config.Config; import com.ss.editor.config.EditorConfig; import com.ss.editor.executor.impl.JMEThreadExecutor; @@ -77,12 +79,12 @@ public void done(final LightProbe result) { private static final Editor EDITOR = new Editor(); /** - * Gets instance. + * Gets the jME part of this editor. * - * @return the instance + * @return the jME part. */ - @NotNull - public static Editor getInstance() { + @FromAnyThread + public static @NotNull Editor getInstance() { return EDITOR; } @@ -91,8 +93,8 @@ public static Editor getInstance() { * * @return the editor */ - @NotNull - static Editor prepareToStart() { + @JMEThread + static @NotNull Editor prepareToStart() { if (Config.DEV_DEBUG) { System.err.println("config is loaded."); @@ -117,6 +119,7 @@ static Editor prepareToStart() { return EDITOR; } + @JMEThread private static void configureLogger() { // disable the standard logger @@ -223,6 +226,7 @@ private Editor() { * * @return the long */ + @FromAnyThread public final long asyncLock() { return lock.readLock(); } @@ -232,11 +236,13 @@ public final long asyncLock() { * * @param stamp the stamp */ + @FromAnyThread public final void asyncUnlock(final long stamp) { lock.unlockRead(stamp); } @Override + @JMEThread public void destroy() { super.destroy(); @@ -247,12 +253,13 @@ public void destroy() { } @Override - @NotNull - public Camera getCamera() { + @FromAnyThread + public @NotNull Camera getCamera() { return super.getCamera(); } @Override + @JMEThread public void simpleInitApp() { super.simpleInitApp(); @@ -336,6 +343,7 @@ public void simpleInitApp() { * * @return the lock stamp. */ + @FromAnyThread public long syncLock() { return lock.writeLock(); } @@ -345,6 +353,7 @@ public long syncLock() { * * @param stamp the stamp of the lock. */ + @FromAnyThread public void syncUnlock(final long stamp) { lock.unlockWrite(stamp); } @@ -354,11 +363,13 @@ public void syncUnlock(final long stamp) { * * @return the long */ + @FromAnyThread public long trySyncLock() { return lock.tryWriteLock(); } @Override + @JMEThread public void loseFocus() { super.loseFocus(); @@ -370,6 +381,7 @@ public void loseFocus() { } @Override + @JMEThread public void gainFocus() { super.gainFocus(); @@ -381,6 +393,7 @@ public void gainFocus() { } @Override + @JMEThread public void simpleUpdate(final float tpf) { super.simpleUpdate(tpf); @@ -389,6 +402,7 @@ public void simpleUpdate(final float tpf) { } @Override + @JMEThread public void update() { final long stamp = syncLock(); try { @@ -413,6 +427,7 @@ public void update() { listener.setRotation(cam.getRotation()); } + @JMEThread private void finishWorkOnError(@NotNull final Throwable e) { GAnalytics.sendException(e, true); @@ -429,14 +444,15 @@ private void finishWorkOnError(@NotNull final Throwable e) { * * @return the processor of post effects. */ - @NotNull - public FilterPostProcessor getPostProcessor() { + @JMEThread + public @NotNull FilterPostProcessor getPostProcessor() { return notNull(postProcessor); } /** * Create the light probes for the PBR render. */ + @JMEThread private void createLightProbes() { final EnvironmentCamera environmentCamera = getEnvironmentCamera(); @@ -470,6 +486,7 @@ private void createLightProbes() { * * @param progressAdapter the progress adapter */ + @JMEThread public void updateLightProbe(@NotNull final JobProgressAdapter progressAdapter) { final LightProbe lightProbe = getLightProbe(); @@ -486,6 +503,7 @@ public void updateLightProbe(@NotNull final JobProgressAdapter progr /** * Disable PBR Light probe. */ + @JMEThread public void disableLightProbe() { final LightProbe lightProbe = getLightProbe(); @@ -498,6 +516,7 @@ public void disableLightProbe() { /** * Enable PBR Light probe. */ + @JMEThread public void enableLightProbe() { final LightProbe lightProbe = getLightProbe(); @@ -522,6 +541,7 @@ public void enableLightProbe() { * * @param progressAdapter the progress adapter */ + @JMEThread public void updatePreviewLightProbe(@NotNull final JobProgressAdapter progressAdapter) { final LightProbe lightProbe = getPreviewLightProbe(); @@ -536,83 +556,101 @@ public void updatePreviewLightProbe(@NotNull final JobProgressAdapter IMAGE_EXTENSIONS = ArrayFactory.asArray( + @NotNull String MODEL_BLENDER = "blend"; + @NotNull String MODEL_FBX = "fbx"; + @NotNull String MODEL_GLTF = "gltf"; + @NotNull String MODEL_OBJ = "obj"; + @NotNull String MODEL_SCENE = "scene"; + @NotNull String MODEL_MESH_XML = "mesh.xml"; + @NotNull String MODEL_XBUF = "xbuf"; + + @NotNull Array IMAGE_EXTENSIONS = ArrayFactory.asArray( IMAGE_PNG, IMAGE_JPG, IMAGE_JPEG, @@ -158,8 +60,7 @@ public interface FileExtensions { IMAGE_GIF, IMAGE_TIFF); - @NotNull - Array TEXTURE_EXTENSIONS = ArrayFactory.asArray( + @NotNull Array TEXTURE_EXTENSIONS = ArrayFactory.asArray( IMAGE_PNG, IMAGE_JPG, IMAGE_JPEG, @@ -174,9 +75,7 @@ public interface FileExtensions { GLSL_GEOM, GLSL_LIB); - @NotNull - Array AUDIO_EXTENSIONS = ArrayFactory.asArray( - AUDIO_MP3, + @NotNull Array AUDIO_EXTENSIONS = ArrayFactory.asArray(AUDIO_MP3, AUDIO_WAV, AUDIO_OGG); } \ No newline at end of file diff --git a/src/main/java/com/ss/editor/FolderAssetLocator.java b/src/main/java/com/ss/editor/FolderAssetLocator.java index c9c40614..12836813 100644 --- a/src/main/java/com/ss/editor/FolderAssetLocator.java +++ b/src/main/java/com/ss/editor/FolderAssetLocator.java @@ -4,6 +4,7 @@ import com.jme3.asset.AssetKey; import com.jme3.asset.AssetLocator; import com.jme3.asset.AssetManager; +import com.ss.editor.annotation.JMEThread; import com.ss.editor.config.EditorConfig; import org.jetbrains.annotations.NotNull; @@ -21,10 +22,12 @@ public class FolderAssetLocator implements AssetLocator { @Override + @JMEThread public void setRootPath(@NotNull final String rootPath) { } @Override + @JMEThread public AssetInfo locate(final AssetManager manager, final AssetKey key) { final EditorConfig editorConfig = EditorConfig.getInstance(); @@ -50,6 +53,7 @@ private PathAssetInfo(@NotNull final AssetManager manager, @NotNull final AssetK } @Override + @JMEThread public @NotNull InputStream openStream() { try { return Files.newInputStream(path, StandardOpenOption.READ); diff --git a/src/main/java/com/ss/editor/JFXApplication.java b/src/main/java/com/ss/editor/JFXApplication.java index 21489a12..993090e6 100644 --- a/src/main/java/com/ss/editor/JFXApplication.java +++ b/src/main/java/com/ss/editor/JFXApplication.java @@ -13,6 +13,7 @@ import com.ss.editor.analytics.google.GAnalytics; import com.ss.editor.annotation.FXThread; import com.ss.editor.annotation.FromAnyThread; +import com.ss.editor.annotation.JMEThread; import com.ss.editor.config.CommandLineConfig; import com.ss.editor.config.Config; import com.ss.editor.config.EditorConfig; @@ -69,9 +70,9 @@ public class JFXApplication extends Application { private static JFXApplication instance; /** - * Gets instance. + * Get the JavaFX part of this editor. * - * @return the instance + * @return the JavaFX part of this editor. */ @FromAnyThread public static @NotNull JFXApplication getInstance() { @@ -154,6 +155,12 @@ public static void main(final String[] args) throws IOException { () -> startJMEApplication(application), "LWJGL Render").start(); } + /** + * Start the new jME application. + * + * @param application the new jME application. + */ + @JMEThread private static void startJMEApplication(@NotNull final JmeToJFXApplication application) { final PluginManager pluginManager = PluginManager.getInstance(); @@ -179,7 +186,8 @@ public static void start() { launch(); } - private static void printError(final Throwable throwable) { + @FromAnyThread + private static void printError(@NotNull final Throwable throwable) { throwable.printStackTrace(); final String userHome = System.getProperty("user.home"); @@ -225,6 +233,7 @@ public JFXApplication() { * * @param window the new opened window. */ + @FXThread public void addWindow(@NotNull final Window window) { ArrayUtils.runInWriteLock(openedWindows, window, Array::add); } @@ -234,6 +243,7 @@ public void addWindow(@NotNull final Window window) { * * @param window the opened window. */ + @FXThread public void removeWindow(@NotNull final Window window) { ArrayUtils.runInWriteLock(openedWindows, window, Array::slowRemove); } @@ -243,11 +253,13 @@ public void removeWindow(@NotNull final Window window) { * * @return the last opened window. */ + @FXThread public @NotNull Window getLastWindow() { return notNull(ArrayUtils.getInReadLock(openedWindows, Array::last)); } @Override + @FXThread public void start(final Stage stage) throws Exception { JFXApplication.instance = this; this.stage = stage; @@ -307,6 +319,7 @@ public void start(final Stage stage) throws Exception { } @Override + @FXThread public void stop() throws Exception { super.stop(); onExit(); @@ -387,6 +400,7 @@ private void buildScene() { }); } + @FXThread private void createSceneProcessor(@NotNull final EditorFXScene scene, @NotNull final Editor editor) { final FrameTransferSceneProcessor sceneProcessor = bind(editor, scene.getCanvas(), editor.getViewPort()); diff --git a/src/main/java/com/ss/editor/Messages.java b/src/main/java/com/ss/editor/Messages.java index b6ed43c4..08dd2d7a 100644 --- a/src/main/java/com/ss/editor/Messages.java +++ b/src/main/java/com/ss/editor/Messages.java @@ -9,2403 +9,659 @@ import java.util.Map; import java.util.ResourceBundle; +/** + * The localized messages. + * + * @author JavaSaBr + */ public class Messages { - /** - * The constant BUNDLE_NAME. - */ public static final String BUNDLE_NAME = "messages/messages"; - /** - * The constant EDITOR_MENU_FILE. - */ public static final String EDITOR_MENU_FILE; - /** - * The constant EDITOR_MENU_FILE_EXIT. - */ public static final String EDITOR_MENU_FILE_EXIT; - /** - * The constant EDITOR_MENU_FILE_OPEN_ASSET. - */ public static final String EDITOR_MENU_FILE_OPEN_ASSET; - /** - * The constant EDITOR_MENU_FILE_OPEN_ASSET_DIRECTORY_CHOOSER. - */ public static final String EDITOR_MENU_FILE_OPEN_ASSET_DIRECTORY_CHOOSER; - /** - * The constant EDITOR_MENU_FILE_REOPEN_ASSET_FOLDER. - */ public static final String EDITOR_MENU_FILE_REOPEN_ASSET_FOLDER; - /** - * The constant EDITOR_MENU_OTHER. - */ public static final String EDITOR_MENU_OTHER; - /** - * The constant EDITOR_MENU_OTHER_CLEAR_ASSET_CACHE. - */ public static final String EDITOR_MENU_OTHER_CLEAR_ASSET_CACHE; - /** - * The constant EDITOR_MENU_OTHER_UPDATE_CLASSPATH_AND_ASSET_CACHE. - */ public static final String EDITOR_MENU_OTHER_UPDATE_CLASSPATH_AND_ASSET_CACHE; - /** - * The constant EDITOR_MENU_OTHER_SETTINGS. - */ public static final String EDITOR_MENU_OTHER_SETTINGS; - /** - * The constant EDITOR_MENU_OTHER_PLUGINS. - */ public static final String EDITOR_MENU_OTHER_PLUGINS; - /** - * The constant EDITOR_MENU_HELP. - */ public static final String EDITOR_MENU_HELP; - /** - * The constant EDITOR_MENU_HELP_ABOUT. - */ public static final String EDITOR_MENU_HELP_ABOUT; - /** - * The constant EDITOR_TOOL_ASSET. - */ public static final String EDITOR_TOOL_ASSET; - /** - * The constant EDITOR_AREA_SAVE_FILE_QUESTION. - */ public static final String EDITOR_AREA_SAVE_FILE_QUESTION; - /** - * The constant ASSET_COMPONENT_RESOURCE_TREE_CONTEXT_MENU_NEW_FILE. - */ public static final String ASSET_COMPONENT_RESOURCE_TREE_CONTEXT_MENU_NEW_FILE; - /** - * The constant ASSET_COMPONENT_RESOURCE_TREE_CONTEXT_MENU_OPEN_FILE. - */ public static final String ASSET_COMPONENT_RESOURCE_TREE_CONTEXT_MENU_OPEN_FILE; - /** - * The constant ASSET_COMPONENT_RESOURCE_TREE_CONTEXT_MENU_OPEN_WITH_FILE. - */ public static final String ASSET_COMPONENT_RESOURCE_TREE_CONTEXT_MENU_OPEN_WITH_FILE; - /** - * The constant ASSET_COMPONENT_RESOURCE_TREE_CONTEXT_MENU_COPY_FILE. - */ public static final String ASSET_COMPONENT_RESOURCE_TREE_CONTEXT_MENU_COPY_FILE; - /** - * The constant ASSET_COMPONENT_RESOURCE_TREE_CONTEXT_MENU_CUT_FILE. - */ public static final String ASSET_COMPONENT_RESOURCE_TREE_CONTEXT_MENU_CUT_FILE; - /** - * The constant ASSET_COMPONENT_RESOURCE_TREE_CONTEXT_MENU_PASTE_FILE. - */ public static final String ASSET_COMPONENT_RESOURCE_TREE_CONTEXT_MENU_PASTE_FILE; - /** - * The constant ASSET_COMPONENT_RESOURCE_TREE_CONTEXT_MENU_DELETE_FILE. - */ public static final String ASSET_COMPONENT_RESOURCE_TREE_CONTEXT_MENU_DELETE_FILE; - /** - * The constant ASSET_COMPONENT_RESOURCE_TREE_CONTEXT_MENU_DELETE_FILE_QUESTION. - */ public static final String ASSET_COMPONENT_RESOURCE_TREE_CONTEXT_MENU_DELETE_FILE_QUESTION; - /** - * The constant ASSET_COMPONENT_RESOURCE_TREE_CONTEXT_MENU_DELETE_FILES_QUESTION. - */ public static final String ASSET_COMPONENT_RESOURCE_TREE_CONTEXT_MENU_DELETE_FILES_QUESTION; - /** - * The constant ASSET_COMPONENT_RESOURCE_TREE_CONTEXT_MENU_CONVERT_FILE. - */ public static final String ASSET_COMPONENT_RESOURCE_TREE_CONTEXT_MENU_CONVERT_FILE; - /** - * The constant ASSET_COMPONENT_RESOURCE_TREE_CONTEXT_MENU_OPEN_FILE_BY_EXTERNAL_EDITOR. - */ public static final String ASSET_COMPONENT_RESOURCE_TREE_CONTEXT_MENU_OPEN_FILE_BY_EXTERNAL_EDITOR; - /** - * The constant ASSET_COMPONENT_RESOURCE_TREE_CONTEXT_MENU_OPEN_FILE_BY_SYSTEM_EXPLORER. - */ public static final String ASSET_COMPONENT_RESOURCE_TREE_CONTEXT_MENU_OPEN_FILE_BY_SYSTEM_EXPLORER; - /** - * The constant ASSET_COMPONENT_RESOURCE_TREE_CONTEXT_MENU_RENAME_FILE. - */ public static final String ASSET_COMPONENT_RESOURCE_TREE_CONTEXT_MENU_RENAME_FILE; - /** - * The constant FILE_EDITOR_ACTION_SAVE. - */ public static final String FILE_EDITOR_ACTION_SAVE; - /** - * The constant SCENE_FILE_EDITOR_ACTION_SELECTION. - */ public static final String SCENE_FILE_EDITOR_ACTION_SELECTION; - /** - * The constant SCENE_FILE_EDITOR_ACTION_GRID. - */ public static final String SCENE_FILE_EDITOR_ACTION_GRID; - /** - * The constant SCENE_FILE_EDITOR_ACTION_STATISTICS. - */ public static final String SCENE_FILE_EDITOR_ACTION_STATISTICS; - /** - * The constant SCENE_FILE_EDITOR_ACTION_MOVE_TOOL. - */ public static final String SCENE_FILE_EDITOR_ACTION_MOVE_TOOL; - /** - * The constant SCENE_FILE_EDITOR_ACTION_SCALE_TOOL. - */ public static final String SCENE_FILE_EDITOR_ACTION_SCALE_TOOL; - /** - * The constant SCENE_FILE_EDITOR_ACTION_ROTATION_TOOL. - */ public static final String SCENE_FILE_EDITOR_ACTION_ROTATION_TOOL; - /** - * The constant SCENE_FILE_EDITOR_ACTION_CAMERA_LIGHT. - */ public static final String SCENE_FILE_EDITOR_ACTION_CAMERA_LIGHT; - /** - * The constant SCENE_FILE_EDITOR_ACTION_PHYSICS. - */ public static final String SCENE_FILE_EDITOR_ACTION_PHYSICS; - /** - * The constant SCENE_FILE_EDITOR_ACTION_DEBUG_PHYSICS. - */ public static final String SCENE_FILE_EDITOR_ACTION_DEBUG_PHYSICS; - /** - * The constant SCENE_FILE_EDITOR_ACTION_SHOW_LIGHTS. - */ public static final String SCENE_FILE_EDITOR_ACTION_SHOW_LIGHTS; - /** - * The constant SCENE_FILE_EDITOR_ACTION_SHOW_AUDIO. - */ public static final String SCENE_FILE_EDITOR_ACTION_SHOW_AUDIO; - /** - * The constant MATERIAL_FILE_EDITOR_ACTION_CUBE. - */ public static final String MATERIAL_FILE_EDITOR_ACTION_CUBE; - /** - * The constant MATERIAL_FILE_EDITOR_ACTION_SPHERE. - */ public static final String MATERIAL_FILE_EDITOR_ACTION_SPHERE; - /** - * The constant MATERIAL_FILE_EDITOR_ACTION_PLANE. - */ public static final String MATERIAL_FILE_EDITOR_ACTION_PLANE; - /** - * The constant MATERIAL_FILE_EDITOR_ACTION_LIGHT. - */ public static final String MATERIAL_FILE_EDITOR_ACTION_LIGHT; - /** - * The constant ASSET_EDITOR_DIALOG_TITLE. - */ public static final String ASSET_EDITOR_DIALOG_TITLE; - /** - * The constant ASSET_EDITOR_DIALOG_WARNING_SELECT_FILE. - */ public static final String ASSET_EDITOR_DIALOG_WARNING_SELECT_FILE; - /** - * The constant SAVE_AS_EDITOR_DIALOG_TITLE. - */ public static final String SAVE_AS_EDITOR_DIALOG_TITLE; - /** - * The constant SAVE_AS_EDITOR_DIALOG_FIELD_FILENAME. - */ public static final String SAVE_AS_EDITOR_DIALOG_FIELD_FILENAME; - /** - * The constant PARTICLE_ASSET_EDITOR_DIALOG_TEXTURE_PARAM_LABEL. - */ public static final String PARTICLE_ASSET_EDITOR_DIALOG_TEXTURE_PARAM_LABEL; - /** - * The constant PARTICLE_ASSET_EDITOR_DIALOG_LIGHTING_TRANSFORM_LABEL. - */ public static final String PARTICLE_ASSET_EDITOR_DIALOG_LIGHTING_TRANSFORM_LABEL; - /** - * The constant MATERIAL_EDITOR_MATERIAL_TYPE_LABEL. - */ public static final String MATERIAL_EDITOR_MATERIAL_TYPE_LABEL; - /** - * The constant MATERIAL_FILE_EDITOR_BUCKET_TYPE_LABEL. - */ public static final String MATERIAL_FILE_EDITOR_BUCKET_TYPE_LABEL; - /** - * The constant TEXTURE_2D_MATERIAL_PARAM_CONTROL_REPEAT. - */ public static final String TEXTURE_2D_MATERIAL_PARAM_CONTROL_REPEAT; - /** - * The constant TEXTURE_2D_MATERIAL_PARAM_CONTROL_FLIP. - */ public static final String TEXTURE_2D_MATERIAL_PARAM_CONTROL_FLIP; - /** - * The constant TEXTURE_2D_MATERIAL_PARAM_CONTROL_ADD. - */ public static final String TEXTURE_2D_MATERIAL_PARAM_CONTROL_ADD; - /** - * The constant TEXTURE_2D_MATERIAL_PARAM_CONTROL_REMOVE. - */ public static final String TEXTURE_2D_MATERIAL_PARAM_CONTROL_REMOVE; - /** - * The constant COLOR_MATERIAL_PARAM_CONTROL_REMOVE. - */ public static final String COLOR_MATERIAL_PARAM_CONTROL_REMOVE; - /** - * The constant MATERIAL_SETTINGS_MAIN. - */ public static final String MATERIAL_SETTINGS_MAIN; - /** - * The constant MATERIAL_SETTINGS_TEXTURES. - */ public static final String MATERIAL_SETTINGS_TEXTURES; - /** - * The constant MATERIAL_SETTINGS_COLORS. - */ public static final String MATERIAL_SETTINGS_COLORS; - /** - * The constant MATERIAL_SETTINGS_RENDER. - */ public static final String MATERIAL_SETTINGS_RENDER; - /** - * The constant MATERIAL_SETTINGS_OTHER. - */ public static final String MATERIAL_SETTINGS_OTHER; - /** - * The constant MATERIAL_RENDER_STATE_FACE_CULL_MODE. - */ public static final String MATERIAL_RENDER_STATE_FACE_CULL_MODE; - /** - * The constant MATERIAL_RENDER_STATE_BLEND_MODE. - */ public static final String MATERIAL_RENDER_STATE_BLEND_MODE; - /** - * The constant MATERIAL_RENDER_STATE_BLEND_EQUATION. - */ public static final String MATERIAL_RENDER_STATE_BLEND_EQUATION; - /** - * The constant MATERIAL_RENDER_STATE_BLEND_EQUATION_ALPHA. - */ public static final String MATERIAL_RENDER_STATE_BLEND_EQUATION_ALPHA; - /** - * The constant MATERIAL_RENDER_STATE_POLY_OFFSET_FACTOR. - */ public static final String MATERIAL_RENDER_STATE_POLY_OFFSET_FACTOR; - /** - * The constant MATERIAL_RENDER_STATE_POLY_OFFSET_UNITS. - */ public static final String MATERIAL_RENDER_STATE_POLY_OFFSET_UNITS; - /** - * The constant MATERIAL_RENDER_STATE_DEPTH_WRITE. - */ public static final String MATERIAL_RENDER_STATE_DEPTH_WRITE; - /** - * The constant MATERIAL_RENDER_STATE_COLOR_WRITE. - */ public static final String MATERIAL_RENDER_STATE_COLOR_WRITE; - /** - * The constant MATERIAL_RENDER_STATE_DEPTH_TEST. - */ public static final String MATERIAL_RENDER_STATE_DEPTH_TEST; - /** - * The constant MATERIAL_RENDER_STATE_WIREFRAME. - */ public static final String MATERIAL_RENDER_STATE_WIREFRAME; - /** - * The constant TEXT_FILE_EDITOR_NAME. - */ public static final String TEXT_FILE_EDITOR_NAME; - /** - * The constant MATERIAL_EDITOR_NAME. - */ public static final String MATERIAL_EDITOR_NAME; - /** - * The constant FILE_CREATOR_FILE_NAME_LABEL. - */ public static final String FILE_CREATOR_FILE_NAME_LABEL; - /** - * The constant MATERIAL_FILE_CREATOR_TITLE. - */ public static final String MATERIAL_FILE_CREATOR_TITLE; - /** - * The constant MATERIAL_FILE_CREATOR_MATERIAL_TYPE_LABEL. - */ public static final String MATERIAL_FILE_CREATOR_MATERIAL_TYPE_LABEL; - /** - * The constant MATERIAL_FILE_CREATOR_FILE_DESCRIPTION. - */ public static final String MATERIAL_FILE_CREATOR_FILE_DESCRIPTION; - /** - * The constant MATERIAL_DEFINITION_FILE_CREATOR_FILE_DESCRIPTION. - */ public static final String MATERIAL_DEFINITION_FILE_CREATOR_FILE_DESCRIPTION; - /** - * The constant MATERIAL_DEFINITION_FILE_CREATOR_TITLE. - */ public static final String MATERIAL_DEFINITION_FILE_CREATOR_TITLE; - /** - * The constant MATERIAL_DEFINITION_FILE_CREATOR_GLSL_LABEL. - */ public static final String MATERIAL_DEFINITION_FILE_CREATOR_GLSL_LABEL; - /** - * The constant SINGLE_COLOR_TEXTURE_FILE_CREATOR_TITLE. - */ public static final String SINGLE_COLOR_TEXTURE_FILE_CREATOR_TITLE; - /** - * The constant SINGLE_COLOR_TEXTURE_FILE_CREATOR_WIDTH. - */ public static final String SINGLE_COLOR_TEXTURE_FILE_CREATOR_WIDTH; - /** - * The constant SINGLE_COLOR_TEXTURE_FILE_CREATOR_HEIGHT. - */ public static final String SINGLE_COLOR_TEXTURE_FILE_CREATOR_HEIGHT; - /** - * The constant SINGLE_COLOR_TEXTURE_FILE_CREATOR_COLOR. - */ public static final String SINGLE_COLOR_TEXTURE_FILE_CREATOR_COLOR; - /** - * The constant SINGLE_COLOR_TEXTURE_FILE_CREATOR_DESCRIPTION. - */ public static final String SINGLE_COLOR_TEXTURE_FILE_CREATOR_DESCRIPTION; - /** - * The constant SETTINGS_DIALOG_TITLE. - */ public static final String SETTINGS_DIALOG_TITLE; - /** - * The constant SETTINGS_DIALOG_FXAA. - */ public static final String SETTINGS_DIALOG_FXAA; - /** - * The constant SETTINGS_DIALOG_NATIVE_FILE_CHOOSER. - */ public static final String SETTINGS_DIALOG_NATIVE_FILE_CHOOSER; - /** - * The constant SETTINGS_DIALOG_STOP_RENDER_ON_LOST_FOCUS. - */ public static final String SETTINGS_DIALOG_STOP_RENDER_ON_LOST_FOCUS; - /** - * The constant SETTINGS_DIALOG_FRAME_RATE. - */ public static final String SETTINGS_DIALOG_FRAME_RATE; - /** - * The constant SETTINGS_DIALOG_GAMMA_CORRECTION. - */ public static final String SETTINGS_DIALOG_GAMMA_CORRECTION; - /** - * The constant SETTINGS_DIALOG_TONEMAP_FILTER. - */ public static final String SETTINGS_DIALOG_TONEMAP_FILTER; - /** - * The constant SETTINGS_DIALOG_TONEMAP_FILTER_WHITE_POINT. - */ public static final String SETTINGS_DIALOG_TONEMAP_FILTER_WHITE_POINT; - /** - * The constant SETTINGS_DIALOG_ANISOTROPY. - */ public static final String SETTINGS_DIALOG_ANISOTROPY; - /** - * The constant SETTINGS_DIALOG_MESSAGE. - */ public static final String SETTINGS_DIALOG_MESSAGE; - /** - * The constant SETTINGS_DIALOG_GOOGLE_ANALYTICS. - */ public static final String SETTINGS_DIALOG_GOOGLE_ANALYTICS; - /** - * The constant SETTINGS_DIALOG_CAMERA_ANGLE. - */ public static final String SETTINGS_DIALOG_CAMERA_ANGLE; - /** - * The constant SETTINGS_DIALOG_AUTO_TANGENT_GENERATING. - */ public static final String SETTINGS_DIALOG_AUTO_TANGENT_GENERATING; - /** - * The constant SETTINGS_DIALOG_DEFAULT_FLIPPED_TEXTURE. - */ public static final String SETTINGS_DIALOG_DEFAULT_FLIPPED_TEXTURE; - /** - * The constant SETTINGS_DIALOG_DEFAULT_EDITOR_CAMERA_LAMP_ENABLED. - */ public static final String SETTINGS_DIALOG_DEFAULT_EDITOR_CAMERA_LAMP_ENABLED; - /** - * The constant SETTINGS_DIALOG_TAB_GRAPHICS. - */ public static final String SETTINGS_DIALOG_TAB_GRAPHICS; - /** - * The constant SETTINGS_DIALOG_TAB_OTHER. - */ public static final String SETTINGS_DIALOG_TAB_OTHER; - /** - * The constant SETTINGS_DIALOG_USER_LIBRARIES_FOLDER_LABEL. - */ public static final String SETTINGS_DIALOG_USER_LIBRARIES_FOLDER_LABEL; - /** - * The constant SETTINGS_DIALOG_USER_CLASSES_FOLDER_LABEL. - */ public static final String SETTINGS_DIALOG_USER_CLASSES_FOLDER_LABEL; - /** - * The constant SETTINGS_DIALOG_THEME_LABEL. - */ public static final String SETTINGS_DIALOG_THEME_LABEL; - /** - * The constant SETTINGS_DIALOG_OPEN_GL_LABEL. - */ public static final String SETTINGS_DIALOG_OPEN_GL_LABEL; - /** - * The constant SETTINGS_DIALOG_CLASSES_FOLDER_CHOOSER_TITLE. - */ public static final String SETTINGS_DIALOG_CLASSES_FOLDER_CHOOSER_TITLE; - /** - * The constant SETTINGS_DIALOG_LIBRARIES_FOLDER_CHOOSER_TITLE. - */ public static final String SETTINGS_DIALOG_LIBRARIES_FOLDER_CHOOSER_TITLE; - /** - * The constant SETTINGS_DIALOG_ENVS_FOLDER_LABEL. - */ public static final String SETTINGS_DIALOG_ENVS_FOLDER_LABEL; - /** - * The constant SETTINGS_DIALOG_ENVS_FOLDER_CHOOSER_TITLE. - */ public static final String SETTINGS_DIALOG_ENVS_FOLDER_CHOOSER_TITLE; - /** - * The constant BLEND_TO_J3O_FILE_CONVERTER_DESCRIPTION. - */ public static final String BLEND_TO_J3O_FILE_CONVERTER_DESCRIPTION; - /** - * The constant GLTF_TO_J3O_FILE_CONVERTER_DESCRIPTION. - */ public static final String GLTF_TO_J3O_FILE_CONVERTER_DESCRIPTION; - /** - * The constant FBX_TO_J3O_FILE_CONVERTER_DESCRIPTION. - */ public static final String FBX_TO_J3O_FILE_CONVERTER_DESCRIPTION; - /** - * The constant OBJ_TO_J3O_FILE_CONVERTER_DESCRIPTION. - */ public static final String OBJ_TO_J3O_FILE_CONVERTER_DESCRIPTION; - /** - * The constant XBUF_TO_J3O_FILE_CONVERTER_DESCRIPTION. - */ public static final String XBUF_TO_J3O_FILE_CONVERTER_DESCRIPTION; - /** - * The constant SCENE_TO_J3O_FILE_CONVERTER_DESCRIPTION. - */ public static final String SCENE_TO_J3O_FILE_CONVERTER_DESCRIPTION; - /** - * The constant MESH_XML_TO_J3O_FILE_CONVERTER_DESCRIPTION. - */ public static final String MESH_XML_TO_J3O_FILE_CONVERTER_DESCRIPTION; - /** - * The constant MODEL_FILE_EDITOR_NAME. - */ public static final String MODEL_FILE_EDITOR_NAME; - /** - * The constant MODEL_FILE_EDITOR_NO_SKY. - */ public static final String MODEL_FILE_EDITOR_NO_SKY; - /** - * The constant MODEL_FILE_EDITOR_FAST_SKY. - */ public static final String MODEL_FILE_EDITOR_FAST_SKY; - /** - * The constant MODEL_FILE_EDITOR_TRANSFORM_MODE. - */ public static final String MODEL_FILE_EDITOR_TRANSFORM_MODE; - /** - * The constant MODEL_FILE_EDITOR_NODE_MESH. - */ public static final String MODEL_FILE_EDITOR_NODE_MESH; - /** - * The constant MODEL_FILE_EDITOR_NODE_MATERIAL. - */ public static final String MODEL_FILE_EDITOR_NODE_MATERIAL; - /** - * The constant MODEL_FILE_EDITOR_NODE_AMBIENT_LIGHT. - */ public static final String MODEL_FILE_EDITOR_NODE_AMBIENT_LIGHT; - /** - * The constant MODEL_FILE_EDITOR_NODE_DIRECTION_LIGHT. - */ public static final String MODEL_FILE_EDITOR_NODE_DIRECTION_LIGHT; - /** - * The constant MODEL_FILE_EDITOR_NODE_POINT_LIGHT. - */ public static final String MODEL_FILE_EDITOR_NODE_POINT_LIGHT; - /** - * The constant MODEL_FILE_EDITOR_NODE_SPOT_LIGHT. - */ public static final String MODEL_FILE_EDITOR_NODE_SPOT_LIGHT; - /** - * The constant MODEL_FILE_EDITOR_NODE_LIGHT_PROBE. - */ public static final String MODEL_FILE_EDITOR_NODE_LIGHT_PROBE; - /** - * The constant MODEL_FILE_EDITOR_NODE_ANIM_CONTROL. - */ public static final String MODEL_FILE_EDITOR_NODE_ANIM_CONTROL; - /** - * The constant MODEL_FILE_EDITOR_NODE_PARTICLE_EMITTER_INFLUENCERS. - */ public static final String MODEL_FILE_EDITOR_NODE_PARTICLE_EMITTER_INFLUENCERS; - /** - * The constant MODEL_FILE_EDITOR_NODE_PARTICLE_EMITTER_INFLUENCER_EMPTY. - */ public static final String MODEL_FILE_EDITOR_NODE_PARTICLE_EMITTER_INFLUENCER_EMPTY; - /** - * The constant MODEL_FILE_EDITOR_NODE_PARTICLE_EMITTER_INFLUENCER_DEFAULT. - */ public static final String MODEL_FILE_EDITOR_NODE_PARTICLE_EMITTER_INFLUENCER_DEFAULT; - /** - * The constant MODEL_FILE_EDITOR_NODE_PARTICLE_EMITTER_INFLUENCER_RADIAL. - */ public static final String MODEL_FILE_EDITOR_NODE_PARTICLE_EMITTER_INFLUENCER_RADIAL; - /** - * The constant MODEL_FILE_EDITOR_NODE_PARTICLE_EMITTER_SHAPE_BOX. - */ public static final String MODEL_FILE_EDITOR_NODE_PARTICLE_EMITTER_SHAPE_BOX; - /** - * The constant MODEL_FILE_EDITOR_NODE_PARTICLE_EMITTER_SHAPE_SPHERE. - */ public static final String MODEL_FILE_EDITOR_NODE_PARTICLE_EMITTER_SHAPE_SPHERE; - /** - * The constant MODEL_FILE_EDITOR_NODE_PARTICLE_EMITTER_SHAPE_POINT. - */ public static final String MODEL_FILE_EDITOR_NODE_PARTICLE_EMITTER_SHAPE_POINT; - /** - * The constant MODEL_FILE_EDITOR_NODE_PARTICLE_EMITTER_SHAPE_MESH_VERTEX. - */ public static final String MODEL_FILE_EDITOR_NODE_PARTICLE_EMITTER_SHAPE_MESH_VERTEX; - /** - * The constant MODEL_FILE_EDITOR_NODE_PARTICLE_EMITTER_SHAPE_MESH_FACE. - */ public static final String MODEL_FILE_EDITOR_NODE_PARTICLE_EMITTER_SHAPE_MESH_FACE; - /** - * The constant MODEL_FILE_EDITOR_NODE_PARTICLE_EMITTER_SHAPE_MESH_CONVEX_HULL. - */ public static final String MODEL_FILE_EDITOR_NODE_PARTICLE_EMITTER_SHAPE_MESH_CONVEX_HULL; - /** - * The constant MODEL_FILE_EDITOR_NODE_STATIC_RIGID_BODY_CONTROL. - */ public static final String MODEL_FILE_EDITOR_NODE_STATIC_RIGID_BODY_CONTROL; - /** - * The constant MODEL_FILE_EDITOR_NODE_RIGID_BODY_CONTROL. - */ public static final String MODEL_FILE_EDITOR_NODE_RIGID_BODY_CONTROL; - /** - * The constant MODEL_FILE_EDITOR_NODE_CHARACTER_CONTROL. - */ public static final String MODEL_FILE_EDITOR_NODE_CHARACTER_CONTROL; - /** - * The constant MODEL_FILE_EDITOR_NODE_SKELETON_CONTROL. - */ public static final String MODEL_FILE_EDITOR_NODE_SKELETON_CONTROL; - /** - * The constant MODEL_FILE_EDITOR_NODE_VEHICLE_CONTROL. - */ public static final String MODEL_FILE_EDITOR_NODE_VEHICLE_CONTROL; - /** - * The constant MODEL_FILE_EDITOR_NODE_RAGDOLL_CONTROL. - */ public static final String MODEL_FILE_EDITOR_NODE_RAGDOLL_CONTROL; - /** - * The constant MODEL_FILE_EDITOR_NODE_BOX_COLLISION_SHAPE. - */ public static final String MODEL_FILE_EDITOR_NODE_BOX_COLLISION_SHAPE; - /** - * The constant MODEL_FILE_EDITOR_NODE_CAPSULE_COLLISION_SHAPE. - */ public static final String MODEL_FILE_EDITOR_NODE_CAPSULE_COLLISION_SHAPE; - /** - * The constant MODEL_FILE_EDITOR_NODE_CHILD_COLLISION_SHAPE. - */ public static final String MODEL_FILE_EDITOR_NODE_CHILD_COLLISION_SHAPE; - /** - * The constant MODEL_FILE_EDITOR_NODE_COMPUTED_COLLISION_SHAPE. - */ public static final String MODEL_FILE_EDITOR_NODE_COMPUTED_COLLISION_SHAPE; - /** - * The constant MODEL_FILE_EDITOR_NODE_CONE_COLLISION_SHAPE. - */ public static final String MODEL_FILE_EDITOR_NODE_CONE_COLLISION_SHAPE; - /** - * The constant MODEL_FILE_EDITOR_NODE_CYLINDER_COLLISION_SHAPE. - */ public static final String MODEL_FILE_EDITOR_NODE_CYLINDER_COLLISION_SHAPE; - /** - * The constant MODEL_FILE_EDITOR_NODE_GIMPACT_COLLISION_SHAPE. - */ public static final String MODEL_FILE_EDITOR_NODE_GIMPACT_COLLISION_SHAPE; - /** - * The constant MODEL_FILE_EDITOR_NODE_HEIGHT_FIELD_COLLISION_SHAPE. - */ public static final String MODEL_FILE_EDITOR_NODE_HEIGHT_FIELD_COLLISION_SHAPE; - /** - * The constant MODEL_FILE_EDITOR_NODE_HULL_COLLISION_SHAPE. - */ public static final String MODEL_FILE_EDITOR_NODE_HULL_COLLISION_SHAPE; - /** - * The constant MODEL_FILE_EDITOR_NODE_MESH_COLLISION_SHAPE. - */ public static final String MODEL_FILE_EDITOR_NODE_MESH_COLLISION_SHAPE; - /** - * The constant MODEL_FILE_EDITOR_NODE_PLANE_COLLISION_SHAPE. - */ public static final String MODEL_FILE_EDITOR_NODE_PLANE_COLLISION_SHAPE; - /** - * The constant MODEL_FILE_EDITOR_NODE_SPHERE_COLLISION_SHAPE. - */ public static final String MODEL_FILE_EDITOR_NODE_SPHERE_COLLISION_SHAPE; - /** - * The constant MODEL_FILE_EDITOR_NODE_WHEEL. - */ public static final String MODEL_FILE_EDITOR_NODE_WHEEL; - /** - * The constant MODEL_FILE_EDITOR_NODE_MOTION_CONTROL. - */ public static final String MODEL_FILE_EDITOR_NODE_MOTION_CONTROL; - /** - * The constant MODEL_FILE_EDITOR_NODE_MOTION_PATH. - */ public static final String MODEL_FILE_EDITOR_NODE_MOTION_PATH; - /** - * The constant MODEL_FILE_EDITOR_NODE_WAY_POINT. - */ public static final String MODEL_FILE_EDITOR_NODE_WAY_POINT; - /** - * The constant MODEL_FILE_EDITOR_NODE_VERTEX_BUFFER. - */ public static final String MODEL_FILE_EDITOR_NODE_VERTEX_BUFFER; - /** - * The constant SCENE_FILE_EDITOR_NAME. - */ public static final String SCENE_FILE_EDITOR_NAME; - /** - * The constant SCENE_FILE_EDITOR_TOOL_OBJECTS. - */ public static final String SCENE_FILE_EDITOR_TOOL_OBJECTS; - /** - * The constant SCENE_FILE_EDITOR_TOOL_EDITING. - */ public static final String SCENE_FILE_EDITOR_TOOL_EDITING; - /** - * The constant SCENE_FILE_EDITOR_TOOL_SCRIPTING. - */ public static final String SCENE_FILE_EDITOR_TOOL_SCRIPTING; - /** - * The constant SCENE_FILE_EDITOR_TOOL_APP_STATES. - */ public static final String SCENE_FILE_EDITOR_TOOL_APP_STATES; - /** - * The constant SCENE_FILE_EDITOR_TOOL_FILTERS. - */ public static final String SCENE_FILE_EDITOR_TOOL_FILTERS; - /** - * The constant SCENE_FILE_EDITOR_TOOL_LAYERS. - */ public static final String SCENE_FILE_EDITOR_TOOL_LAYERS; - /** - * The constant MODEL_NODE_TREE_ACTION_REMOVE. - */ public static final String MODEL_NODE_TREE_ACTION_REMOVE; - /** - * The constant MODEL_NODE_TREE_ACTION_RENAME. - */ public static final String MODEL_NODE_TREE_ACTION_RENAME; - /** - * The constant MODEL_NODE_TREE_ACTION_OPTIMIZE_GEOMETRY. - */ public static final String MODEL_NODE_TREE_ACTION_OPTIMIZE_GEOMETRY; - /** - * The constant MODEL_NODE_TREE_ACTION_TOOLS. - */ public static final String MODEL_NODE_TREE_ACTION_TOOLS; - /** - * The constant MODEL_NODE_TREE_ACTION_CREATE. - */ public static final String MODEL_NODE_TREE_ACTION_CREATE; - /** - * The constant MODEL_NODE_TREE_ACTION_CREATE_NODE. - */ public static final String MODEL_NODE_TREE_ACTION_CREATE_NODE; - /** - * The constant MODEL_NODE_TREE_ACTION_CREATE_SKY. - */ public static final String MODEL_NODE_TREE_ACTION_CREATE_SKY; - /** - * The constant MODEL_NODE_TREE_ACTION_CREATE_EDITABLE_SKY. - */ public static final String MODEL_NODE_TREE_ACTION_CREATE_EDITABLE_SKY; - /** - * The constant MODEL_NODE_TREE_ACTION_CREATE_PRIMITIVE. - */ public static final String MODEL_NODE_TREE_ACTION_CREATE_PRIMITIVE; - /** - * The constant MODEL_NODE_TREE_ACTION_CREATE_PRIMITIVE_BOX. - */ public static final String MODEL_NODE_TREE_ACTION_CREATE_PRIMITIVE_BOX; - /** - * The constant MODEL_NODE_TREE_ACTION_CREATE_PRIMITIVE_SPHERE. - */ public static final String MODEL_NODE_TREE_ACTION_CREATE_PRIMITIVE_SPHERE; - /** - * The constant MODEL_NODE_TREE_ACTION_CREATE_PRIMITIVE_QUAD. - */ public static final String MODEL_NODE_TREE_ACTION_CREATE_PRIMITIVE_QUAD; - /** - * The constant MODEL_NODE_TREE_ACTION_LOAD_MODEL. - */ public static final String MODEL_NODE_TREE_ACTION_LOAD_MODEL; - /** - * The constant MODEL_NODE_TREE_ACTION_SAVE_AS. - */ public static final String MODEL_NODE_TREE_ACTION_SAVE_AS; - /** - * The constant MODEL_NODE_TREE_ACTION_MAKE_EMBEDDED. - */ public static final String MODEL_NODE_TREE_ACTION_MAKE_EMBEDDED; - /** - * The constant MODEL_NODE_TREE_ACTION_LINK_MODEL. - */ public static final String MODEL_NODE_TREE_ACTION_LINK_MODEL; - /** - * The constant MODEL_NODE_TREE_ACTION_TANGENT_GENERATOR. - */ public static final String MODEL_NODE_TREE_ACTION_TANGENT_GENERATOR; - /** - * The constant MODEL_NODE_TREE_ACTION_LOD_GENERATOR. - */ public static final String MODEL_NODE_TREE_ACTION_LOD_GENERATOR; - /** - * The constant MODEL_NODE_TREE_ACTION_LIGHT. - */ public static final String MODEL_NODE_TREE_ACTION_LIGHT; - /** - * The constant MODEL_NODE_TREE_ACTION_AMBIENT_LIGHT. - */ public static final String MODEL_NODE_TREE_ACTION_AMBIENT_LIGHT; - /** - * The constant MODEL_NODE_TREE_ACTION_DIRECTION_LIGHT. - */ public static final String MODEL_NODE_TREE_ACTION_DIRECTION_LIGHT; - /** - * The constant MODEL_NODE_TREE_ACTION_POINT_LIGHT. - */ public static final String MODEL_NODE_TREE_ACTION_POINT_LIGHT; - /** - * The constant MODEL_NODE_TREE_ACTION_SPOT_LIGHT. - */ public static final String MODEL_NODE_TREE_ACTION_SPOT_LIGHT; - /** - * The constant MODEL_NODE_TREE_ACTION_ANIMATION_PLAY. - */ public static final String MODEL_NODE_TREE_ACTION_ANIMATION_PLAY; - /** - * The constant MODEL_NODE_TREE_ACTION_ANIMATION_PLAY_SETTINGS. - */ public static final String MODEL_NODE_TREE_ACTION_ANIMATION_PLAY_SETTINGS; - /** - * The constant MODEL_NODE_TREE_ACTION_ANIMATION_STOP. - */ public static final String MODEL_NODE_TREE_ACTION_ANIMATION_STOP; - /** - * The constant MODEL_NODE_TREE_ACTION_ANIMATION_PAUSE. - */ public static final String MODEL_NODE_TREE_ACTION_ANIMATION_PAUSE; - /** - * The constant MODEL_NODE_TREE_ACTION_ANIMATION_MANUAL_EXTRAXT_SUB_ANIMATION. - */ public static final String MODEL_NODE_TREE_ACTION_ANIMATION_MANUAL_EXTRAXT_SUB_ANIMATION; - /** - * The constant MODEL_NODE_TREE_ACTION_CREATE_AUDIO_NODE. - */ public static final String MODEL_NODE_TREE_ACTION_CREATE_AUDIO_NODE; - /** - * The constant MODEL_NODE_TREE_ACTION_AUDIO_PLAY. - */ public static final String MODEL_NODE_TREE_ACTION_AUDIO_PLAY; - /** - * The constant MODEL_NODE_TREE_ACTION_AUDIO_STOP. - */ public static final String MODEL_NODE_TREE_ACTION_AUDIO_STOP; - /** - * The constant MODEL_NODE_TREE_ACTION_CREATE_TONEG0D_PARTICLE_EMITTER. - */ public static final String MODEL_NODE_TREE_ACTION_CREATE_TONEG0D_PARTICLE_EMITTER; - /** - * The constant MODEL_NODE_TREE_ACTION_CREATE_SOFT_TONEG0D_PARTICLE_EMITTER. - */ public static final String MODEL_NODE_TREE_ACTION_CREATE_SOFT_TONEG0D_PARTICLE_EMITTER; - /** - * The constant MODEL_NODE_TREE_ACTION_CREATE_DEFAULT_PARTICLE_EMITTER. - */ public static final String MODEL_NODE_TREE_ACTION_CREATE_DEFAULT_PARTICLE_EMITTER; - /** - * The constant MODEL_NODE_TREE_ACTION_RESET_PARTICLE_EMITTERS. - */ public static final String MODEL_NODE_TREE_ACTION_RESET_PARTICLE_EMITTERS; - /** - * The constant MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_TRIANGLE_SHAPE. - */ public static final String MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_TRIANGLE_SHAPE; - /** - * The constant MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_CHANGE_SHAPE. - */ public static final String MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_CHANGE_SHAPE; - /** - * The constant MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_CHANGE_INFLUENCER. - */ public static final String MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_CHANGE_INFLUENCER; - /** - * The constant MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_INFLUENCER_DEFAULT. - */ public static final String MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_INFLUENCER_DEFAULT; - /** - * The constant MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_INFLUENCER_EMPTY. - */ public static final String MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_INFLUENCER_EMPTY; - /** - * The constant MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_INFLUENCER_RADIAL. - */ public static final String MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_INFLUENCER_RADIAL; - /** - * The constant MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_POINT_SHAPE. - */ public static final String MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_POINT_SHAPE; - /** - * The constant MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_BOX_SHAPE. - */ public static final String MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_BOX_SHAPE; - /** - * The constant MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_SPHERE_SHAPE. - */ public static final String MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_SPHERE_SHAPE; - /** - * The constant MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_MESH_VERTEX_SHAPE. - */ public static final String MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_MESH_VERTEX_SHAPE; - /** - * The constant MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_MESH_FACE_SHAPE. - */ public static final String MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_MESH_FACE_SHAPE; - /** - * The constant MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_MESH_CONVEX_HULL_SHAPE. - */ public static final String MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_MESH_CONVEX_HULL_SHAPE; - /** - * The constant MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_CYLINDER_SHAPE. - */ public static final String MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_CYLINDER_SHAPE; - /** - * The constant MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_DOME_SHAPE. - */ public static final String MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_DOME_SHAPE; - /** - * The constant MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_QUAD_SHAPE. - */ public static final String MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_QUAD_SHAPE; - /** - * The constant MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_TORUS_SHAPE. - */ public static final String MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_TORUS_SHAPE; - /** - * The constant MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_MODEL_SHAPE. - */ public static final String MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_MODEL_SHAPE; - /** - * The constant MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_CHANGE_PARTICLES_MESH. - */ public static final String MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_CHANGE_PARTICLES_MESH; - /** - * The constant MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_PARTICLES_MESH_QUAD. - */ public static final String MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_PARTICLES_MESH_QUAD; - /** - * The constant MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_PARTICLES_MESH_POINT. - */ public static final String MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_PARTICLES_MESH_POINT; - /** - * The constant MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_PARTICLES_MESH_IMPOSTOR. - */ public static final String MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_PARTICLES_MESH_IMPOSTOR; - /** - * The constant MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_PARTICLES_MESH_MODEL. - */ public static final String MODEL_NODE_TREE_ACTION_PARTICLE_EMITTER_PARTICLES_MESH_MODEL; - /** - * The constant MODEL_NODE_TREE_ACTION_CREATE_LAYER. - */ public static final String MODEL_NODE_TREE_ACTION_CREATE_LAYER; - /** - * The constant MODEL_NODE_TREE_ACTION_ADD_USER_DATA. - */ public static final String MODEL_NODE_TREE_ACTION_ADD_USER_DATA; - /** - * The constant MODEL_NODE_TREE_ACTION_ADD_CONTROL. - */ public static final String MODEL_NODE_TREE_ACTION_ADD_CONTROL; - /** - * The constant MODEL_NODE_TREE_ACTION_ADD_CONTROL_RIGID_BODY. - */ public static final String MODEL_NODE_TREE_ACTION_ADD_CONTROL_RIGID_BODY; - /** - * The constant MODEL_NODE_TREE_ACTION_ADD_CONTROL_STATIC_RIGID_BODY. - */ public static final String MODEL_NODE_TREE_ACTION_ADD_CONTROL_STATIC_RIGID_BODY; - /** - * The constant MODEL_NODE_TREE_ACTION_ADD_CONTROL_NOTION. - */ public static final String MODEL_NODE_TREE_ACTION_ADD_CONTROL_NOTION; - /** - * The constant MODEL_NODE_TREE_ACTION_ADD_CONTROL_CHARACTER. - */ public static final String MODEL_NODE_TREE_ACTION_ADD_CONTROL_CHARACTER; - /** - * The constant MODEL_NODE_TREE_ACTION_ADD_CONTROL_CUSTOM. - */ public static final String MODEL_NODE_TREE_ACTION_ADD_CONTROL_CUSTOM; - /** - * The constant MODEL_NODE_TREE_ACTION_ADD_CONTROL_VEHICLE. - */ public static final String MODEL_NODE_TREE_ACTION_ADD_CONTROL_VEHICLE; - /** - * The constant MODEL_NODE_TREE_ACTION_ADD_CONTROL_KINEMATIC_RAGDOLL. - */ public static final String MODEL_NODE_TREE_ACTION_ADD_CONTROL_KINEMATIC_RAGDOLL; - /** - * The constant MODEL_NODE_TREE_ACTION_REACTIVATE. - */ public static final String MODEL_NODE_TREE_ACTION_REACTIVATE; - /** - * The constant MODEL_NODE_TREE_ACTION_CHANGE_COLLISION_SHAPE. - */ public static final String MODEL_NODE_TREE_ACTION_CHANGE_COLLISION_SHAPE; - /** - * The constant MODEL_NODE_TREE_ACTION_GENERATE_COLLISION_SHAPE. - */ public static final String MODEL_NODE_TREE_ACTION_GENERATE_COLLISION_SHAPE; - /** - * The constant MODEL_NODE_TREE_ACTION_BOX_COLLISION_SHAPE. - */ public static final String MODEL_NODE_TREE_ACTION_BOX_COLLISION_SHAPE; - /** - * The constant MODEL_NODE_TREE_ACTION_CAPSULE_COLLISION_SHAPE. - */ public static final String MODEL_NODE_TREE_ACTION_CAPSULE_COLLISION_SHAPE; - /** - * The constant MODEL_NODE_TREE_ACTION_CONE_COLLISION_SHAPE. - */ public static final String MODEL_NODE_TREE_ACTION_CONE_COLLISION_SHAPE; - /** - * The constant MODEL_NODE_TREE_ACTION_CYLINDER_COLLISION_SHAPE. - */ public static final String MODEL_NODE_TREE_ACTION_CYLINDER_COLLISION_SHAPE; - /** - * The constant MODEL_NODE_TREE_ACTION_SPHERE_COLLISION_SHAPE. - */ public static final String MODEL_NODE_TREE_ACTION_SPHERE_COLLISION_SHAPE; - /** - * The constant MODEL_NODE_TREE_ACTION_ADD_WHEEL. - */ public static final String MODEL_NODE_TREE_ACTION_ADD_WHEEL; - /** - * The constant MODEL_NODE_TREE_ACTION_ADD_TERRAIN. - */ public static final String MODEL_NODE_TREE_ACTION_ADD_TERRAIN; - /** - * The constant MODEL_PROPERTY_CULL_HINT. - */ public static final String MODEL_PROPERTY_CULL_HINT; - /** - * The constant MODEL_PROPERTY_SHADOW_MODE. - */ public static final String MODEL_PROPERTY_SHADOW_MODE; - /** - * The constant MODEL_PROPERTY_QUEUE_BUCKET. - */ public static final String MODEL_PROPERTY_QUEUE_BUCKET; - /** - * The constant MODEL_PROPERTY_LOCATION. - */ public static final String MODEL_PROPERTY_LOCATION; - /** - * The constant MODEL_PROPERTY_SCALE. - */ public static final String MODEL_PROPERTY_SCALE; - /** - * The constant MODEL_PROPERTY_ROTATION. - */ public static final String MODEL_PROPERTY_ROTATION; - /** - * The constant MODEL_PROPERTY_MATERIAL. - */ public static final String MODEL_PROPERTY_MATERIAL; - /** - * The constant MODEL_PROPERTY_DIRECTION. - */ public static final String MODEL_PROPERTY_DIRECTION; - /** - * The constant MODEL_PROPERTY_RADIUS. - */ public static final String MODEL_PROPERTY_RADIUS; - /** - * The constant MODEL_PROPERTY_COLOR. - */ public static final String MODEL_PROPERTY_COLOR; - /** - * The constant MODEL_PROPERTY_INNER_ANGLE. - */ public static final String MODEL_PROPERTY_INNER_ANGLE; - /** - * The constant MODEL_PROPERTY_OUTER_ANGLE. - */ public static final String MODEL_PROPERTY_OUTER_ANGLE; - /** - * The constant MODEL_PROPERTY_MIN. - */ public static final String MODEL_PROPERTY_MIN; - /** - * The constant MODEL_PROPERTY_MAX. - */ public static final String MODEL_PROPERTY_MAX; - /** - * The constant MODEL_PROPERTY_IS_LOOPING. - */ public static final String MODEL_PROPERTY_IS_LOOPING; - /** - * The constant MODEL_PROPERTY_IS_REVERB. - */ public static final String MODEL_PROPERTY_IS_REVERB; - /** - * The constant MODEL_PROPERTY_IS_DIRECTIONAL. - */ public static final String MODEL_PROPERTY_IS_DIRECTIONAL; - /** - * The constant MODEL_PROPERTY_IS_POSITIONAL. - */ public static final String MODEL_PROPERTY_IS_POSITIONAL; - /** - * The constant MODEL_PROPERTY_AUDIO_PITCH. - */ public static final String MODEL_PROPERTY_AUDIO_PITCH; - /** - * The constant MODEL_PROPERTY_AUDIO_VOLUME. - */ public static final String MODEL_PROPERTY_AUDIO_VOLUME; - /** - * The constant MODEL_PROPERTY_TIME_OFFSET. - */ public static final String MODEL_PROPERTY_TIME_OFFSET; - /** - * The constant MODEL_PROPERTY_MAX_DISTANCE. - */ public static final String MODEL_PROPERTY_MAX_DISTANCE; - /** - * The constant MODEL_PROPERTY_REF_DISTANCE. - */ public static final String MODEL_PROPERTY_REF_DISTANCE; - /** - * The constant MODEL_PROPERTY_AUDIO_DATA. - */ public static final String MODEL_PROPERTY_AUDIO_DATA; - /** - * The constant MODEL_PROPERTY_VELOCITY. - */ public static final String MODEL_PROPERTY_VELOCITY; - /** - * The constant MODEL_PROPERTY_LOD. - */ public static final String MODEL_PROPERTY_LOD; - /** - * The constant MODEL_PROPERTY_TRIANGLE_COUNT. - */ public static final String MODEL_PROPERTY_TRIANGLE_COUNT; - /** - * The constant MODEL_PROPERTY_LEVEL. - */ public static final String MODEL_PROPERTY_LEVEL; - /** - * The constant MODEL_PROPERTY_LAYER. - */ public static final String MODEL_PROPERTY_LAYER; - /** - * The constant MODEL_PROPERTY_VALUE. - */ public static final String MODEL_PROPERTY_VALUE; - /** - * The constant MODEL_PROPERTY_ID. - */ public static final String MODEL_PROPERTY_ID; - /** - * The constant MODEL_PROPERTY_INSTANCE_COUNT. - */ public static final String MODEL_PROPERTY_INSTANCE_COUNT; - /** - * The constant MODEL_PROPERTY_VERTEX_COUNT. - */ public static final String MODEL_PROPERTY_VERTEX_COUNT; - /** - * The constant MODEL_PROPERTY_NUM_LOD_LEVELS. - */ public static final String MODEL_PROPERTY_NUM_LOD_LEVELS; - /** - * The constant MODEL_PROPERTY_MODE. - */ public static final String MODEL_PROPERTY_MODE; - /** - * The constant MODEL_PROPERTY_TYPE. - */ public static final String MODEL_PROPERTY_TYPE; - /** - * The constant MODEL_PROPERTY_FORMAT. - */ public static final String MODEL_PROPERTY_FORMAT; - /** - * The constant MODEL_PROPERTY_USAGE. - */ public static final String MODEL_PROPERTY_USAGE; - /** - * The constant MODEL_PROPERTY_UNIQ_ID. - */ public static final String MODEL_PROPERTY_UNIQ_ID; - /** - * The constant MODEL_PROPERTY_BASE_INSTANCE_COUNT. - */ public static final String MODEL_PROPERTY_BASE_INSTANCE_COUNT; - /** - * The constant MODEL_PROPERTY_INSTANCE_SPAN. - */ public static final String MODEL_PROPERTY_INSTANCE_SPAN; - /** - * The constant MODEL_PROPERTY_NUM_COMPONENTS. - */ public static final String MODEL_PROPERTY_NUM_COMPONENTS; - /** - * The constant MODEL_PROPERTY_NUM_ELEMENTS. - */ public static final String MODEL_PROPERTY_NUM_ELEMENTS; - /** - * The constant MODEL_PROPERTY_OFFSET. - */ public static final String MODEL_PROPERTY_OFFSET; - /** - * The constant MODEL_PROPERTY_STRIDE. - */ public static final String MODEL_PROPERTY_STRIDE; - /** - * The constant MODEL_PROPERTY_CAPACITY. - */ public static final String MODEL_PROPERTY_CAPACITY; - /** - * The constant MODEL_PROPERTY_IS_ENABLED. - */ public static final String MODEL_PROPERTY_IS_ENABLED; - /** - * The constant MODEL_PROPERTY_IS_HARDWARE_SKINNING_PREFERRED. - */ public static final String MODEL_PROPERTY_IS_HARDWARE_SKINNING_PREFERRED; - /** - * The constant MODEL_PROPERTY_VIEW_DIRECTION. - */ public static final String MODEL_PROPERTY_VIEW_DIRECTION; - /** - * The constant MODEL_PROPERTY_WALK_DIRECTION. - */ public static final String MODEL_PROPERTY_WALK_DIRECTION; - /** - * The constant MODEL_PROPERTY_FALL_SPEED. - */ public static final String MODEL_PROPERTY_FALL_SPEED; - /** - * The constant MODEL_PROPERTY_GRAVITY. - */ public static final String MODEL_PROPERTY_GRAVITY; - /** - * The constant MODEL_PROPERTY_JUMP_SPEED. - */ public static final String MODEL_PROPERTY_JUMP_SPEED; - /** - * The constant MODEL_PROPERTY_MAX_SLOPE. - */ public static final String MODEL_PROPERTY_MAX_SLOPE; - /** - * The constant MODEL_PROPERTY_IS_APPLY_PHYSICS_LOCAL. - */ public static final String MODEL_PROPERTY_IS_APPLY_PHYSICS_LOCAL; - /** - * The constant MODEL_PROPERTY_IS_USE_VIEW_DIRECTION. - */ public static final String MODEL_PROPERTY_IS_USE_VIEW_DIRECTION; - /** - * The constant MODEL_PROPERTY_IS_KINEMATIC_SPATIAL. - */ public static final String MODEL_PROPERTY_IS_KINEMATIC_SPATIAL; - /** - * The constant MODEL_PROPERTY_IS_KINEMATIC. - */ public static final String MODEL_PROPERTY_IS_KINEMATIC; - /** - * The constant MODEL_PROPERTY_ANGULAR_VELOCITY. - */ public static final String MODEL_PROPERTY_ANGULAR_VELOCITY; - /** - * The constant MODEL_PROPERTY_LINEAR_FACTOR. - */ public static final String MODEL_PROPERTY_LINEAR_FACTOR; - /** - * The constant MODEL_PROPERTY_ANGULAR_DAMPING. - */ public static final String MODEL_PROPERTY_ANGULAR_DAMPING; - /** - * The constant MODEL_PROPERTY_ANGULAR_FACTOR. - */ public static final String MODEL_PROPERTY_ANGULAR_FACTOR; - /** - * The constant MODEL_PROPERTY_FRICTION. - */ public static final String MODEL_PROPERTY_FRICTION; - /** - * The constant MODEL_PROPERTY_LINEAR_DAMPING. - */ public static final String MODEL_PROPERTY_LINEAR_DAMPING; - /** - * The constant MODEL_PROPERTY_MASS. - */ public static final String MODEL_PROPERTY_MASS; - /** - * The constant MODEL_PROPERTY_RESTITUTION. - */ public static final String MODEL_PROPERTY_RESTITUTION; - /** - * The constant MODEL_PROPERTY_CURRENT_VALUE. - */ public static final String MODEL_PROPERTY_CURRENT_VALUE; - /** - * The constant MODEL_PROPERTY_CURRENT_WAY_POINT. - */ public static final String MODEL_PROPERTY_CURRENT_WAY_POINT; - /** - * The constant MODEL_PROPERTY_DIRECTION_TYPE. - */ public static final String MODEL_PROPERTY_DIRECTION_TYPE; - /** - * The constant MODEL_PROPERTY_ANGULAR_SLEEPING_THRESHOLD. - */ public static final String MODEL_PROPERTY_ANGULAR_SLEEPING_THRESHOLD; - /** - * The constant MODEL_PROPERTY_LOOP_MODE. - */ public static final String MODEL_PROPERTY_LOOP_MODE; - /** - * The constant MODEL_PROPERTY_INITIAL_DURATION. - */ public static final String MODEL_PROPERTY_INITIAL_DURATION; - /** - * The constant MODEL_PROPERTY_SPEED. - */ public static final String MODEL_PROPERTY_SPEED; - /** - * The constant MODEL_PROPERTY_TIME. - */ public static final String MODEL_PROPERTY_TIME; - /** - * The constant MODEL_PROPERTY_MARGIN. - */ public static final String MODEL_PROPERTY_MARGIN; - /** - * The constant MODEL_PROPERTY_HALF_EXTENTS. - */ public static final String MODEL_PROPERTY_HALF_EXTENTS; - /** - * The constant MODEL_PROPERTY_HEIGHT. - */ public static final String MODEL_PROPERTY_HEIGHT; - /** - * The constant MODEL_PROPERTY_AXIS. - */ public static final String MODEL_PROPERTY_AXIS; - /** - * The constant MODEL_PROPERTY_OBJECT_ID. - */ public static final String MODEL_PROPERTY_OBJECT_ID; - /** - * The constant MODEL_PROPERTY_AXLE. - */ public static final String MODEL_PROPERTY_AXLE; - /** - * The constant MODEL_PROPERTY_REST_LENGTH. - */ public static final String MODEL_PROPERTY_REST_LENGTH; - /** - * The constant MODEL_PROPERTY_IS_FRONT. - */ public static final String MODEL_PROPERTY_IS_FRONT; - /** - * The constant MODEL_PROPERTY_DAMPING_COMPRESSION. - */ public static final String MODEL_PROPERTY_DAMPING_COMPRESSION; - /** - * The constant MODEL_PROPERTY_FRICTION_SLIP. - */ public static final String MODEL_PROPERTY_FRICTION_SLIP; - /** - * The constant MODEL_PROPERTY_MAX_SUSPENSION_FORCE. - */ public static final String MODEL_PROPERTY_MAX_SUSPENSION_FORCE; - /** - * The constant MODEL_PROPERTY_MAX_SUSPENSION_TRAVEL_CM. - */ public static final String MODEL_PROPERTY_MAX_SUSPENSION_TRAVEL_CM; - /** - * The constant MODEL_PROPERTY_DAMPING_RELAXATION. - */ public static final String MODEL_PROPERTY_DAMPING_RELAXATION; - /** - * The constant MODEL_PROPERTY_SUSPENSION_STIFFNESS. - */ public static final String MODEL_PROPERTY_SUSPENSION_STIFFNESS; - /** - * The constant MODEL_PROPERTY_ROLL_INFLUENCE. - */ public static final String MODEL_PROPERTY_ROLL_INFLUENCE; - /** - * The constant MODEL_PROPERTY_WHEEL_SPATIAL. - */ public static final String MODEL_PROPERTY_WHEEL_SPATIAL; - /** - * The constant MODEL_PROPERTY_LENGTH. - */ public static final String MODEL_PROPERTY_LENGTH; - /** - * The constant MODEL_PROPERTY_CURRENT_TIME. - */ public static final String MODEL_PROPERTY_CURRENT_TIME; - /** - * The constant MODEL_PROPERTY_POINT. - */ public static final String MODEL_PROPERTY_POINT; - /** - * The constant MODEL_PROPERTY_CENTER. - */ public static final String MODEL_PROPERTY_CENTER; - /** - * The constant MODEL_PROPERTY_VELOCITY_VARIATION. - */ public static final String MODEL_PROPERTY_VELOCITY_VARIATION; - /** - * The constant MODEL_PROPERTY_INITIAL_VELOCITY. - */ public static final String MODEL_PROPERTY_INITIAL_VELOCITY; - /** - * The constant MODEL_PROPERTY_ORIGIN. - */ public static final String MODEL_PROPERTY_ORIGIN; - /** - * The constant MODEL_PROPERTY_RADIAL_VELOCITY. - */ public static final String MODEL_PROPERTY_RADIAL_VELOCITY; - /** - * The constant MODEL_PROPERTY_IS_HORIZONTAL. - */ public static final String MODEL_PROPERTY_IS_HORIZONTAL; - /** - * The constant MODEL_PROPERTY_IS_TEST_MODE. - */ public static final String MODEL_PROPERTY_IS_TEST_MODE; - /** - * The constant MODEL_PROPERTY_IS_FACING_VELOCITY. - */ public static final String MODEL_PROPERTY_IS_FACING_VELOCITY; - /** - * The constant MODEL_PROPERTY_IS_IN_WORLD_SPACE. - */ public static final String MODEL_PROPERTY_IS_IN_WORLD_SPACE; - /** - * The constant MODEL_PROPERTY_IS_RANDOM_ANGLE. - */ public static final String MODEL_PROPERTY_IS_RANDOM_ANGLE; - /** - * The constant MODEL_PROPERTY_IS_SELECT_RANDOM_IMAGE. - */ public static final String MODEL_PROPERTY_IS_SELECT_RANDOM_IMAGE; - /** - * The constant MODEL_PROPERTY_SIZE. - */ public static final String MODEL_PROPERTY_SIZE; - /** - * The constant MODEL_PROPERTY_ROTATE_SPEED. - */ public static final String MODEL_PROPERTY_ROTATE_SPEED; - /** - * The constant MODEL_PROPERTY_START_COLOR. - */ public static final String MODEL_PROPERTY_START_COLOR; - /** - * The constant MODEL_PROPERTY_END_COLOR. - */ public static final String MODEL_PROPERTY_END_COLOR; - /** - * The constant MODEL_PROPERTY_MESH_TYPE. - */ public static final String MODEL_PROPERTY_MESH_TYPE; - /** - * The constant MODEL_PROPERTY_FACE_NORMAL. - */ public static final String MODEL_PROPERTY_FACE_NORMAL; - /** - * The constant MODEL_PROPERTY_IS_RANDOM_POINT. - */ public static final String MODEL_PROPERTY_IS_RANDOM_POINT; - /** - * The constant MODEL_PROPERTY_IS_SEQUENTIAL_FACE. - */ public static final String MODEL_PROPERTY_IS_SEQUENTIAL_FACE; - /** - * The constant MODEL_PROPERTY_IS_SKIP_PATTERN. - */ public static final String MODEL_PROPERTY_IS_SKIP_PATTERN; - /** - * The constant MODEL_PROPERTY_EMISSION_POINT. - */ public static final String MODEL_PROPERTY_EMISSION_POINT; - /** - * The constant MODEL_PROPERTY_MAX_PARTICLES. - */ public static final String MODEL_PROPERTY_MAX_PARTICLES; - /** - * The constant MODEL_PROPERTY_EMISSION_PER_SECOND. - */ public static final String MODEL_PROPERTY_EMISSION_PER_SECOND; - /** - * The constant MODEL_PROPERTY_PARTICLES_PER_SECOND. - */ public static final String MODEL_PROPERTY_PARTICLES_PER_SECOND; - /** - * The constant MODEL_PROPERTY_EMITTER_LIFE. - */ public static final String MODEL_PROPERTY_EMITTER_LIFE; - /** - * The constant MODEL_PROPERTY_EMITTER_DELAY. - */ public static final String MODEL_PROPERTY_EMITTER_DELAY; - /** - * The constant MODEL_PROPERTY_IS_TEST_PARTICLES. - */ public static final String MODEL_PROPERTY_IS_TEST_PARTICLES; - /** - * The constant MODEL_PROPERTY_IS_FOLLOW_EMITTER. - */ public static final String MODEL_PROPERTY_IS_FOLLOW_EMITTER; - /** - * The constant MODEL_PROPERTY_STRETCHING. - */ public static final String MODEL_PROPERTY_STRETCHING; - /** - * The constant MODEL_PROPERTY_MAGNITUDE. - */ public static final String MODEL_PROPERTY_MAGNITUDE; - /** - * The constant MODEL_PROPERTY_BILLBOARD. - */ public static final String MODEL_PROPERTY_BILLBOARD; - /** - * The constant MODEL_PROPERTY_INITIAL_FORCE. - */ public static final String MODEL_PROPERTY_INITIAL_FORCE; - /** - * The constant MODEL_PROPERTY_LIFE. - */ public static final String MODEL_PROPERTY_LIFE; - /** - * The constant MODEL_PROPERTY_COLUMNS. - */ public static final String MODEL_PROPERTY_COLUMNS; - /** - * The constant MODEL_PROPERTY_ROWS. - */ public static final String MODEL_PROPERTY_ROWS; - /** - * The constant MODEL_PROPERTY_SPRITE_COUNT. - */ public static final String MODEL_PROPERTY_SPRITE_COUNT; - /** - * The constant MODEL_PROPERTY_FIXED_DURATION. - */ public static final String MODEL_PROPERTY_FIXED_DURATION; - /** - * The constant MODEL_PROPERTY_IS_RANDOM_START_COLOR. - */ public static final String MODEL_PROPERTY_IS_RANDOM_START_COLOR; - /** - * The constant MODEL_PROPERTY_IS_RANDOM_START_SIZE. - */ public static final String MODEL_PROPERTY_IS_RANDOM_START_SIZE; - /** - * The constant MODEL_PROPERTY_SIZE_VARIATION_FACTOR. - */ public static final String MODEL_PROPERTY_SIZE_VARIATION_FACTOR; - /** - * The constant MODEL_PROPERTY_IS_RANDOM_START_DESTINATION. - */ public static final String MODEL_PROPERTY_IS_RANDOM_START_DESTINATION; - /** - * The constant MODEL_PROPERTY_CHANCE. - */ public static final String MODEL_PROPERTY_CHANCE; - /** - * The constant MODEL_PROPERTY_STRENGTH. - */ public static final String MODEL_PROPERTY_STRENGTH; - /** - * The constant MODEL_PROPERTY_ALIGNMENT. - */ public static final String MODEL_PROPERTY_ALIGNMENT; - /** - * The constant MODEL_PROPERTY_IS_RANDOM_DIRECTION. - */ public static final String MODEL_PROPERTY_IS_RANDOM_DIRECTION; - /** - * The constant MODEL_PROPERTY_PULL_CENTER. - */ public static final String MODEL_PROPERTY_PULL_CENTER; - /** - * The constant MODEL_PROPERTY_PULL_ALIGNMENT. - */ public static final String MODEL_PROPERTY_PULL_ALIGNMENT; - /** - * The constant MODEL_PROPERTY_UP_ALIGNMENT. - */ public static final String MODEL_PROPERTY_UP_ALIGNMENT; - /** - * The constant MODEL_PROPERTY_RADIAL_PULL. - */ public static final String MODEL_PROPERTY_RADIAL_PULL; - /** - * The constant MODEL_PROPERTY_TANGENT_FORCE. - */ public static final String MODEL_PROPERTY_TANGENT_FORCE; - /** - * The constant MODEL_PROPERTY_ALPHA_INTERPOLATION. - */ public static final String MODEL_PROPERTY_ALPHA_INTERPOLATION; - /** - * The constant MODEL_PROPERTY_COLOR_INTERPOLATION. - */ public static final String MODEL_PROPERTY_COLOR_INTERPOLATION; - /** - * The constant MODEL_PROPERTY_DESTINATION_INTERPOLATION. - */ public static final String MODEL_PROPERTY_DESTINATION_INTERPOLATION; - /** - * The constant MODEL_PROPERTY_ROTATION_INTERPOLATION. - */ public static final String MODEL_PROPERTY_ROTATION_INTERPOLATION; - /** - * The constant MODEL_PROPERTY_SIZE_INTERPOLATION. - */ public static final String MODEL_PROPERTY_SIZE_INTERPOLATION; - /** - * The constant MODEL_PROPERTY_ALPHA. - */ public static final String MODEL_PROPERTY_ALPHA; - /** - * The constant MODEL_PROPERTY_FRAME_SEQUENCE. - */ public static final String MODEL_PROPERTY_FRAME_SEQUENCE; - /** - * The constant MODEL_PROPERTY_IS_RANDOM_START_IMAGE. - */ public static final String MODEL_PROPERTY_IS_RANDOM_START_IMAGE; - /** - * The constant MODEL_PROPERTY_IS_ANIMATE. - */ public static final String MODEL_PROPERTY_IS_ANIMATE; - /** - * The constant MODEL_PROPERTY_REACTION. - */ public static final String MODEL_PROPERTY_REACTION; - /** - * The constant MODEL_PROPERTY_IS_RANDOM_SPEED. - */ public static final String MODEL_PROPERTY_IS_RANDOM_SPEED; - /** - * The constant MODEL_PROPERTY_IS_START_RANDOM_ROTATION_X. - */ public static final String MODEL_PROPERTY_IS_START_RANDOM_ROTATION_X; - /** - * The constant MODEL_PROPERTY_INTERPOLATION. - */ public static final String MODEL_PROPERTY_INTERPOLATION; - /** - * The constant MODEL_PROPERTY_GEOMETRY_LIST. - */ public static final String MODEL_PROPERTY_GEOMETRY_LIST; - /** - * The constant MODEL_PROPERTY_GEOMETRY. - */ public static final String MODEL_PROPERTY_GEOMETRY; - /** - * The constant MODEL_PROPERTY_AXIS_SAMPLES. - */ public static final String MODEL_PROPERTY_AXIS_SAMPLES; - /** - * The constant MODEL_PROPERTY_RADIAL_SAMPLES. - */ public static final String MODEL_PROPERTY_RADIAL_SAMPLES; - /** - * The constant MODEL_PROPERTY_PLANES. - */ public static final String MODEL_PROPERTY_PLANES; - /** - * The constant MODEL_PROPERTY_WIDTH. - */ public static final String MODEL_PROPERTY_WIDTH; - /** - * The constant MODEL_PROPERTY_FLIP_COORDS. - */ public static final String MODEL_PROPERTY_FLIP_COORDS; - /** - * The constant MODEL_PROPERTY_Z_SAMPLES. - */ public static final String MODEL_PROPERTY_Z_SAMPLES; - /** - * The constant MODEL_PROPERTY_CIRCLE_SAMPLES. - */ public static final String MODEL_PROPERTY_CIRCLE_SAMPLES; - /** - * The constant MODEL_PROPERTY_INNER_RADIUS. - */ public static final String MODEL_PROPERTY_INNER_RADIUS; - /** - * The constant MODEL_PROPERTY_OUTER_RADIUS. - */ public static final String MODEL_PROPERTY_OUTER_RADIUS; - /** - * The constant MODEL_PROPERTY_NAME. - */ public static final String MODEL_PROPERTY_NAME; - /** - * The constant MODEL_PROPERTY_DATA_TYPE. - */ public static final String MODEL_PROPERTY_DATA_TYPE; - /** - * The constant MATERIAL_MODEL_PROPERTY_CONTROL_NO_TEXTURE. - */ public static final String MATERIAL_MODEL_PROPERTY_CONTROL_NO_TEXTURE; - /** - * The constant MATERIAL_MODEL_PROPERTY_CONTROL_TEXTURE_SETTINGS. - */ public static final String MATERIAL_MODEL_PROPERTY_CONTROL_TEXTURE_SETTINGS; - /** - * The constant MATERIAL_MODEL_PROPERTY_CONTROL_NO_MATERIAL. - */ public static final String MATERIAL_MODEL_PROPERTY_CONTROL_NO_MATERIAL; - /** - * The constant MATERIAL_MODEL_PROPERTY_CONTROL_FLIP_Y. - */ public static final String MATERIAL_MODEL_PROPERTY_CONTROL_FLIP_Y; - /** - * The constant MATERIAL_MODEL_PROPERTY_CONTROL_WRAP_MODE_S. - */ public static final String MATERIAL_MODEL_PROPERTY_CONTROL_WRAP_MODE_S; - /** - * The constant MATERIAL_MODEL_PROPERTY_CONTROL_WRAP_MODE_T. - */ public static final String MATERIAL_MODEL_PROPERTY_CONTROL_WRAP_MODE_T; - /** - * The constant MATERIAL_MODEL_PROPERTY_CONTROL_MAG_FILTER. - */ public static final String MATERIAL_MODEL_PROPERTY_CONTROL_MAG_FILTER; - /** - * The constant MATERIAL_MODEL_PROPERTY_CONTROL_MIN_FILTER. - */ public static final String MATERIAL_MODEL_PROPERTY_CONTROL_MIN_FILTER; - /** - * The constant ABSTRACT_ELEMENT_PROPERTY_CONTROL_NO_ELEMENT. - */ public static final String ABSTRACT_ELEMENT_PROPERTY_CONTROL_NO_ELEMENT; - /** - * The constant LAYER_PROPERTY_CONTROL_NO_LAYER. - */ public static final String LAYER_PROPERTY_CONTROL_NO_LAYER; - /** - * The constant AUDIO_KEY_PROPERTY_CONTROL_NO_AUDIO. - */ public static final String AUDIO_KEY_PROPERTY_CONTROL_NO_AUDIO; - /** - * The constant CHOOSE_FOLDER_CONTROL_NO_FOLDER. - */ public static final String CHOOSE_FOLDER_CONTROL_NO_FOLDER; - /** - * The constant RENAME_DIALOG_TITLE. - */ public static final String RENAME_DIALOG_TITLE; - /** - * The constant RENAME_DIALOG_NEW_NAME_LABEL. - */ public static final String RENAME_DIALOG_NEW_NAME_LABEL; - /** - * The constant RENAME_DIALOG_BUTTON_OK. - */ public static final String RENAME_DIALOG_BUTTON_OK; - /** - * The constant PLAY_ANIMATION_SETTINGS_DIALOG_TITLE. - */ public static final String PLAY_ANIMATION_SETTINGS_DIALOG_TITLE; - /** - * The constant MANUAL_EXTRACT_ANIMATION_DIALOG_TITLE. - */ public static final String MANUAL_EXTRACT_ANIMATION_DIALOG_TITLE; - /** - * The constant MANUAL_EXTRACT_ANIMATION_DIALOG_NAME. - */ public static final String MANUAL_EXTRACT_ANIMATION_DIALOG_NAME; - /** - * The constant MANUAL_EXTRACT_ANIMATION_DIALOG_NAME_EXAMPLE. - */ public static final String MANUAL_EXTRACT_ANIMATION_DIALOG_NAME_EXAMPLE; - /** - * The constant MANUAL_EXTRACT_ANIMATION_DIALOG_START_FRAME. - */ public static final String MANUAL_EXTRACT_ANIMATION_DIALOG_START_FRAME; - /** - * The constant MANUAL_EXTRACT_ANIMATION_DIALOG_END_FRAME. - */ public static final String MANUAL_EXTRACT_ANIMATION_DIALOG_END_FRAME; - /** - * The constant MANUAL_EXTRACT_ANIMATION_DIALOG_BUTTON_OK. - */ public static final String MANUAL_EXTRACT_ANIMATION_DIALOG_BUTTON_OK; - /** - * The constant QUESTION_DIALOG_TITLE. - */ public static final String QUESTION_DIALOG_TITLE; - /** - * The constant FOLDER_CREATOR_DESCRIPTION. - */ public static final String FOLDER_CREATOR_DESCRIPTION; - /** - * The constant FOLDER_CREATOR_TITLE. - */ public static final String FOLDER_CREATOR_TITLE; - /** - * The constant FOLDER_CREATOR_FILE_NAME_LABEL. - */ public static final String FOLDER_CREATOR_FILE_NAME_LABEL; - /** - * The constant EMPTY_FILE_CREATOR_DESCRIPTION. - */ public static final String EMPTY_FILE_CREATOR_DESCRIPTION; - /** - * The constant EMPTY_FILE_CREATOR_TITLE. - */ public static final String EMPTY_FILE_CREATOR_TITLE; - /** - * The constant IMAGE_VIEWER_EDITOR_NAME. - */ public static final String IMAGE_VIEWER_EDITOR_NAME; - /** - * The constant AUDIO_VIEWER_EDITOR_NAME. - */ public static final String AUDIO_VIEWER_EDITOR_NAME; - /** - * The constant AUDIO_VIEWER_EDITOR_DURATION_LABEL. - */ public static final String AUDIO_VIEWER_EDITOR_DURATION_LABEL; - /** - * The constant AUDIO_VIEWER_EDITOR_BITS_PER_SAMPLE_LABEL. - */ public static final String AUDIO_VIEWER_EDITOR_BITS_PER_SAMPLE_LABEL; - /** - * The constant AUDIO_VIEWER_EDITOR_CHANNELS_LABEL. - */ public static final String AUDIO_VIEWER_EDITOR_CHANNELS_LABEL; - /** - * The constant AUDIO_VIEWER_EDITOR_DATA_TYPE_LABEL. - */ public static final String AUDIO_VIEWER_EDITOR_DATA_TYPE_LABEL; - /** - * The constant AUDIO_VIEWER_EDITOR_SAMPLE_RATE_LABEL. - */ public static final String AUDIO_VIEWER_EDITOR_SAMPLE_RATE_LABEL; - /** - * The constant CREATE_SKY_DIALOG_TITLE. - */ public static final String CREATE_SKY_DIALOG_TITLE; - /** - * The constant CREATE_SKY_DIALOG_SKY_TYPE_SINGLE. - */ public static final String CREATE_SKY_DIALOG_SKY_TYPE_SINGLE; - /** - * The constant CREATE_SKY_DIALOG_SKY_TYPE_MULTIPLE. - */ public static final String CREATE_SKY_DIALOG_SKY_TYPE_MULTIPLE; - /** - * The constant CREATE_SKY_DIALOG_SKY_TYPE. - */ public static final String CREATE_SKY_DIALOG_SKY_TYPE; - /** - * The constant CREATE_SKY_DIALOG_NORMAL_SCALE. - */ public static final String CREATE_SKY_DIALOG_NORMAL_SCALE; - /** - * The constant CREATE_SKY_DIALOG_MATERIAL_FOLDER. - */ public static final String CREATE_SKY_DIALOG_MATERIAL_FOLDER; - /** - * The constant CREATE_SKY_DIALOG_MATERIAL_NAME. - */ public static final String CREATE_SKY_DIALOG_MATERIAL_NAME; - /** - * The constant CREATE_SKY_DIALOG_TEXTURE. - */ public static final String CREATE_SKY_DIALOG_TEXTURE; - /** - * The constant CREATE_SKY_DIALOG_TEXTURE_TYPE. - */ public static final String CREATE_SKY_DIALOG_TEXTURE_TYPE; - /** - * The constant CREATE_SKY_DIALOG_FLIP_Y. - */ public static final String CREATE_SKY_DIALOG_FLIP_Y; - /** - * The constant CREATE_SKY_DIALOG_NORTH. - */ public static final String CREATE_SKY_DIALOG_NORTH; - /** - * The constant CREATE_SKY_DIALOG_SOUTH. - */ public static final String CREATE_SKY_DIALOG_SOUTH; - /** - * The constant CREATE_SKY_DIALOG_EAST. - */ public static final String CREATE_SKY_DIALOG_EAST; - /** - * The constant CREATE_SKY_DIALOG_WEST. - */ public static final String CREATE_SKY_DIALOG_WEST; - /** - * The constant CREATE_SKY_DIALOG_TOP. - */ public static final String CREATE_SKY_DIALOG_TOP; - /** - * The constant CREATE_SKY_DIALOG_BOTTOM. - */ public static final String CREATE_SKY_DIALOG_BOTTOM; - /** - * The constant SIMPLE_DIALOG_BUTTON_OK. - */ public static final String SIMPLE_DIALOG_BUTTON_OK; - /** - * The constant SIMPLE_DIALOG_BUTTON_SELECT. - */ public static final String SIMPLE_DIALOG_BUTTON_SELECT; - /** - * The constant SIMPLE_DIALOG_BUTTON_ADD. - */ public static final String SIMPLE_DIALOG_BUTTON_ADD; - /** - * The constant SIMPLE_DIALOG_BUTTON_SAVE. - */ public static final String SIMPLE_DIALOG_BUTTON_SAVE; - /** - * The constant SIMPLE_DIALOG_BUTTON_GENERATE. - */ public static final String SIMPLE_DIALOG_BUTTON_GENERATE; - /** - * The constant SIMPLE_DIALOG_BUTTON_CREATE. - */ public static final String SIMPLE_DIALOG_BUTTON_CREATE; - /** - * The constant SIMPLE_DIALOG_BUTTON_APPLY. - */ public static final String SIMPLE_DIALOG_BUTTON_APPLY; - /** - * The constant SIMPLE_DIALOG_BUTTON_CANCEL. - */ public static final String SIMPLE_DIALOG_BUTTON_CANCEL; - /** - * The constant SIMPLE_DIALOG_BUTTON_CLOSE. - */ public static final String SIMPLE_DIALOG_BUTTON_CLOSE; - /** - * The constant SIMPLE_DIALOG_BUTTON_YES. - */ public static final String SIMPLE_DIALOG_BUTTON_YES; - /** - * The constant SIMPLE_DIALOG_BUTTON_NO. - */ public static final String SIMPLE_DIALOG_BUTTON_NO; - /** - * The constant EMPTY_MODEL_CREATOR_DESCRIPTION. - */ public static final String EMPTY_MODEL_CREATOR_DESCRIPTION; - /** - * The constant EMPTY_MODEL_CREATOR_TITLE. - */ public static final String EMPTY_MODEL_CREATOR_TITLE; - /** - * The constant EMPTY_SCENE_CREATOR_DESCRIPTION. - */ public static final String EMPTY_SCENE_CREATOR_DESCRIPTION; - /** - * The constant DEFAULT_SCENE_CREATOR_DESCRIPTION. - */ public static final String DEFAULT_SCENE_CREATOR_DESCRIPTION; - /** - * The constant EMPTY_SCENE_CREATOR_TITLE. - */ public static final String EMPTY_SCENE_CREATOR_TITLE; - /** - * The constant DEFAULT_SCENE_CREATOR_TITLE. - */ public static final String DEFAULT_SCENE_CREATOR_TITLE; - /** - * The constant GLSL_FILE_EDITOR_NAME. - */ public static final String GLSL_FILE_EDITOR_NAME; - /** - * The constant MATERIAL_DEFINITION_FILE_EDITOR_NAME. - */ public static final String MATERIAL_DEFINITION_FILE_EDITOR_NAME; - /** - * The constant GENERATE_TANGENTS_DIALOG_SPLIT_MIRRORED. - */ public static final String GENERATE_TANGENTS_DIALOG_SPLIT_MIRRORED; - /** - * The constant GENERATE_TANGENTS_DIALOG_ALGORITHM_LABEL. - */ public static final String GENERATE_TANGENTS_DIALOG_ALGORITHM_LABEL; - /** - * The constant GENERATE_TANGENTS_DIALOG_TITLE. - */ public static final String GENERATE_TANGENTS_DIALOG_TITLE; - /** - * The constant GENERATE_LOD_DIALOG_TITLE. - */ public static final String GENERATE_LOD_DIALOG_TITLE; - /** - * The constant GENERATE_LOD_DIALOG_METHOD. - */ public static final String GENERATE_LOD_DIALOG_METHOD; - /** - * The constant BOUNDING_VOLUME_MODEL_PROPERTY_CONTROL_NAME. - */ public static final String BOUNDING_VOLUME_MODEL_PROPERTY_CONTROL_NAME; - /** - * The constant BOUNDING_VOLUME_MODEL_PROPERTY_CONTROL_SPHERE. - */ public static final String BOUNDING_VOLUME_MODEL_PROPERTY_CONTROL_SPHERE; - /** - * The constant BOUNDING_VOLUME_MODEL_PROPERTY_CONTROL_SPHERE_RADIUS. - */ public static final String BOUNDING_VOLUME_MODEL_PROPERTY_CONTROL_SPHERE_RADIUS; - /** - * The constant BOUNDING_VOLUME_MODEL_PROPERTY_CONTROL_BOX. - */ public static final String BOUNDING_VOLUME_MODEL_PROPERTY_CONTROL_BOX; - /** - * The constant NODE_SELECTOR_DIALOG_TITLE. - */ public static final String NODE_SELECTOR_DIALOG_TITLE; - /** - * The constant GEOMETRY_SELECTOR_DIALOG_TITLE. - */ public static final String GEOMETRY_SELECTOR_DIALOG_TITLE; - /** - * The constant LIGHT_SELECTOR_DIALOG_TITLE. - */ public static final String LIGHT_SELECTOR_DIALOG_TITLE; - /** - * The constant LOG_VIEW_TITLE. - */ public static final String LOG_VIEW_TITLE; - /** - * The constant CREATE_SCENE_APP_STATE_DIALOG_TITLE. - */ public static final String CREATE_SCENE_APP_STATE_DIALOG_TITLE; - /** - * The constant CREATE_SCENE_APP_STATE_DIALOG_BUILT_IN. - */ public static final String CREATE_SCENE_APP_STATE_DIALOG_BUILT_IN; - /** - * The constant CREATE_SCENE_APP_STATE_DIALOG_CUSTOM_BOX. - */ public static final String CREATE_SCENE_APP_STATE_DIALOG_CUSTOM_BOX; - /** - * The constant CREATE_SCENE_APP_STATE_DIALOG_CUSTOM_FIELD. - */ public static final String CREATE_SCENE_APP_STATE_DIALOG_CUSTOM_FIELD; - /** - * The constant CREATE_SCENE_FILTER_DIALOG_TITLE. - */ public static final String CREATE_SCENE_FILTER_DIALOG_TITLE; - /** - * The constant CREATE_SCENE_FILTER_DIALOG_BUILT_IN. - */ public static final String CREATE_SCENE_FILTER_DIALOG_BUILT_IN; - /** - * The constant CREATE_SCENE_FILTER_DIALOG_CUSTOM_BOX. - */ public static final String CREATE_SCENE_FILTER_DIALOG_CUSTOM_BOX; - /** - * The constant CREATE_SCENE_FILTER_DIALOG_CUSTOM_FIELD. - */ public static final String CREATE_SCENE_FILTER_DIALOG_CUSTOM_FIELD; - /** - * The constant ADD_USER_DATA_DIALOG_TITLE. - */ public static final String ADD_USER_DATA_DIALOG_TITLE; - /** - * The constant CREATE_CUSTOM_CONTROL_DIALOG_TITLE. - */ public static final String CREATE_CUSTOM_CONTROL_DIALOG_TITLE; - /** - * The constant CREATE_CUSTOM_CONTROL_DIALOG_BUILT_IN. - */ public static final String CREATE_CUSTOM_CONTROL_DIALOG_BUILT_IN; - /** - * The constant CREATE_CUSTOM_CONTROL_DIALOG_CUSTOM_BOX. - */ public static final String CREATE_CUSTOM_CONTROL_DIALOG_CUSTOM_BOX; - /** - * The constant CREATE_CUSTOM_CONTROL_DIALOG_CUSTOM_FIELD. - */ public static final String CREATE_CUSTOM_CONTROL_DIALOG_CUSTOM_FIELD; - /** - * The constant ANALYTICS_CONFIRM_DIALOG_MESSAGE. - */ public static final String ANALYTICS_CONFIRM_DIALOG_MESSAGE; - /** - * The constant CREATE_BOX_COLLISION_SHAPE_DIALOG_TITLE. - */ public static final String CREATE_BOX_COLLISION_SHAPE_DIALOG_TITLE; - /** - * The constant CREATE_SPHERE_COLLISION_SHAPE_DIALOG_TITLE. - */ public static final String CREATE_SPHERE_COLLISION_SHAPE_DIALOG_TITLE; - /** - * The constant CREATE_CYLINDER_COLLISION_SHAPE_DIALOG_TITLE. - */ public static final String CREATE_CYLINDER_COLLISION_SHAPE_DIALOG_TITLE; - /** - * The constant CREATE_CONE_COLLISION_SHAPE_DIALOG_TITLE. - */ public static final String CREATE_CONE_COLLISION_SHAPE_DIALOG_TITLE; - /** - * The constant CREATE_CAPSULE_COLLISION_SHAPE_DIALOG_TITLE. - */ public static final String CREATE_CAPSULE_COLLISION_SHAPE_DIALOG_TITLE; - /** - * The constant ADD_VEHICLE_WHEEL_DIALOG_TITLE. - */ public static final String ADD_VEHICLE_WHEEL_DIALOG_TITLE; - /** - * The constant CREATE_TERRAIN_DIALOG_TITLE. - */ public static final String CREATE_TERRAIN_DIALOG_TITLE; - /** - * The constant CREATE_TERRAIN_DIALOG_BASE_TEXTURE. - */ public static final String CREATE_TERRAIN_DIALOG_BASE_TEXTURE; - /** - * The constant CREATE_TERRAIN_DIALOG_FOLDER_ALPHA_TEXTURE. - */ public static final String CREATE_TERRAIN_DIALOG_FOLDER_ALPHA_TEXTURE; - /** - * The constant CREATE_TERRAIN_DIALOG_TOTAL_SIZE. - */ public static final String CREATE_TERRAIN_DIALOG_TOTAL_SIZE; - /** - * The constant CREATE_TERRAIN_DIALOG_PATCH_SIZE. - */ public static final String CREATE_TERRAIN_DIALOG_PATCH_SIZE; - /** - * The constant CREATE_TERRAIN_DIALOG_ALPHA_BLEND_TEXTURE_SIZE. - */ public static final String CREATE_TERRAIN_DIALOG_ALPHA_BLEND_TEXTURE_SIZE; - /** - * The constant CREATE_TERRAIN_DIALOG_TERRAIN_TYPE. - */ public static final String CREATE_TERRAIN_DIALOG_TERRAIN_TYPE; - /** - * The constant CREATE_TERRAIN_DIALOG_TERRAIN_TYPE_FLAT. - */ public static final String CREATE_TERRAIN_DIALOG_TERRAIN_TYPE_FLAT; - /** - * The constant CREATE_TERRAIN_DIALOG_TERRAIN_TYPE_IMAGE_BASED. - */ public static final String CREATE_TERRAIN_DIALOG_TERRAIN_TYPE_IMAGE_BASED; - /** - * The constant CREATE_TERRAIN_DIALOG_TERRAIN_TYPE_HILL. - */ public static final String CREATE_TERRAIN_DIALOG_TERRAIN_TYPE_HILL; - /** - * The constant CREATE_TERRAIN_DIALOG_HEIGHT_MAP_IMAGE. - */ public static final String CREATE_TERRAIN_DIALOG_HEIGHT_MAP_IMAGE; - /** - * The constant CREATE_TERRAIN_DIALOG_HEIGHT_SMOOTH. - */ public static final String CREATE_TERRAIN_DIALOG_HEIGHT_SMOOTH; - /** - * The constant CREATE_TERRAIN_DIALOG_HEIGHT_SCALE. - */ public static final String CREATE_TERRAIN_DIALOG_HEIGHT_SCALE; - /** - * The constant CREATE_TERRAIN_DIALOG_ITERATIONS. - */ public static final String CREATE_TERRAIN_DIALOG_ITERATIONS; - /** - * The constant CREATE_TERRAIN_DIALOG_FLATTENING. - */ public static final String CREATE_TERRAIN_DIALOG_FLATTENING; - /** - * The constant CREATE_TERRAIN_DIALOG_MIN_RADIUS. - */ public static final String CREATE_TERRAIN_DIALOG_MIN_RADIUS; - /** - * The constant CREATE_TERRAIN_DIALOG_MAX_RADIUS. - */ public static final String CREATE_TERRAIN_DIALOG_MAX_RADIUS; - /** - * The constant CREATE_PARTICLE_EMITTER_POINT_SHAPE_DIALOG_TITLE. - */ public static final String CREATE_PARTICLE_EMITTER_POINT_SHAPE_DIALOG_TITLE; - /** - * The constant CREATE_PARTICLE_EMITTER_BOX_SHAPE_DIALOG_TITLE. - */ public static final String CREATE_PARTICLE_EMITTER_BOX_SHAPE_DIALOG_TITLE; - /** - * The constant CREATE_PARTICLE_EMITTER_SPHERE_SHAPE_DIALOG_TITLE. - */ public static final String CREATE_PARTICLE_EMITTER_SPHERE_SHAPE_DIALOG_TITLE; - /** - * The constant CREATE_PARTICLE_EMITTER_CYLINDER_SHAPE_DIALOG_TITLE. - */ public static final String CREATE_PARTICLE_EMITTER_CYLINDER_SHAPE_DIALOG_TITLE; - /** - * The constant CREATE_PARTICLE_EMITTER_DOME_SHAPE_DIALOG_TITLE. - */ public static final String CREATE_PARTICLE_EMITTER_DOME_SHAPE_DIALOG_TITLE; - /** - * The constant CREATE_PARTICLE_EMITTER_QUAD_SHAPE_DIALOG_TITLE. - */ public static final String CREATE_PARTICLE_EMITTER_QUAD_SHAPE_DIALOG_TITLE; - /** - * The constant CREATE_PARTICLE_EMITTER_TORUS_SHAPE_DIALOG_TITLE. - */ public static final String CREATE_PARTICLE_EMITTER_TORUS_SHAPE_DIALOG_TITLE; - /** - * The constant CREATE_PARTICLE_EMITTER_TRIANGLE_SHAPE_DIALOG_TITLE. - */ public static final String CREATE_PARTICLE_EMITTER_TRIANGLE_SHAPE_DIALOG_TITLE; - /** - * The constant EDITING_COMPONENT_BRUSH_SIZE. - */ public static final String EDITING_COMPONENT_BRUSH_SIZE; - /** - * The constant EDITING_COMPONENT_BRUSH_POWER. - */ public static final String EDITING_COMPONENT_BRUSH_POWER; - /** - * The constant EDITING_COMPONENT_SMOOTHLY. - */ public static final String EDITING_COMPONENT_SMOOTHLY; - /** - * The constant EDITING_COMPONENT_LIMITED. - */ public static final String EDITING_COMPONENT_LIMITED; - /** - * The constant EDITING_COMPONENT_USE_MARKER. - */ public static final String EDITING_COMPONENT_USE_MARKER; - /** - * The constant EDITING_COMPONENT_LEVEL. - */ public static final String EDITING_COMPONENT_LEVEL; - /** - * The constant EDITING_COMPONENT_ROUGHNESS. - */ public static final String EDITING_COMPONENT_ROUGHNESS; - /** - * The constant EDITING_COMPONENT_FREQUENCY. - */ public static final String EDITING_COMPONENT_FREQUENCY; - /** - * The constant EDITING_COMPONENT_LACUNARITY. - */ public static final String EDITING_COMPONENT_LACUNARITY; - /** - * The constant EDITING_COMPONENT_OCTAVES. - */ public static final String EDITING_COMPONENT_OCTAVES; - /** - * The constant EDITING_COMPONENT_SCALE. - */ public static final String EDITING_COMPONENT_SCALE; - /** - * The constant EDITING_COMPONENT_TRI_PLANAR. - */ public static final String EDITING_COMPONENT_TRI_PLANAR; - /** - * The constant EDITING_COMPONENT_SHININESS. - */ public static final String EDITING_COMPONENT_SHININESS; - /** - * The constant EDITING_COMPONENT_LAYER. - */ public static final String EDITING_COMPONENT_LAYER; - /** - * The constant MODEL_CONVERTER_DIALOG_TITLE. - */ public static final String MODEL_CONVERTER_DIALOG_TITLE; - /** - * The constant MODEL_CONVERTER_DIALOG_RESULT_NAME. - */ public static final String MODEL_CONVERTER_DIALOG_RESULT_NAME; - /** - * The constant MODEL_CONVERTER_DIALOG_DESTINATION_FOLDER. - */ public static final String MODEL_CONVERTER_DIALOG_DESTINATION_FOLDER; - /** - * The constant MODEL_CONVERTER_DIALOG_EXPORT_MATERIALS. - */ public static final String MODEL_CONVERTER_DIALOG_EXPORT_MATERIALS; - /** - * The constant MODEL_CONVERTER_DIALOG_MATERIAL_FOLDER. - */ public static final String MODEL_CONVERTER_DIALOG_MATERIAL_FOLDER; - /** - * The constant MODEL_CONVERTER_DIALOG_OVERWRITE_MATERIALS. - */ public static final String MODEL_CONVERTER_DIALOG_OVERWRITE_MATERIALS; - /** - * The constant MODEL_CONVERTER_DIALOG_BUTTON_OK. - */ public static final String MODEL_CONVERTER_DIALOG_BUTTON_OK; - /** - * The constant FILE_DELETE_HANDLER_DELETE_MATERIALS. - */ public static final String FILE_DELETE_HANDLER_DELETE_MATERIALS; - /** - * The constant CHECK_NEW_VERSION_DIALOG_TITLE. - */ public static final String CHECK_NEW_VERSION_DIALOG_TITLE; - /** - * The constant CHECK_NEW_VERSION_DIALOG_HYPERLINK. - */ public static final String CHECK_NEW_VERSION_DIALOG_HYPERLINK; - /** - * The constant CHECK_NEW_VERSION_DIALOG_HEADER_TEXT. - */ public static final String CHECK_NEW_VERSION_DIALOG_HEADER_TEXT; - /** - * The constant EDITOR_SCRIPTING_COMPONENT_HEADERS. - */ public static final String EDITOR_SCRIPTING_COMPONENT_HEADERS; - /** - * The constant EDITOR_SCRIPTING_COMPONENT_BODY. - */ public static final String EDITOR_SCRIPTING_COMPONENT_BODY; - /** - * The constant EDITOR_SCRIPTING_COMPONENT_RUN. - */ public static final String EDITOR_SCRIPTING_COMPONENT_RUN; - /** - * The constant ABOUT_DIALOG_TITLE. - */ public static final String ABOUT_DIALOG_TITLE; - /** - * The constant ABOUT_DIALOG_VERSION. - */ public static final String ABOUT_DIALOG_VERSION; - /** - * The constant ABOUT_DIALOG_PROJECT_HOME. - */ public static final String ABOUT_DIALOG_PROJECT_HOME; - /** - * The constant ABOUT_DIALOG_FORUM_THREAD. - */ public static final String ABOUT_DIALOG_FORUM_THREAD; - /** - * The constant ABOUT_DIALOG_USED_LIBRARIES. - */ public static final String ABOUT_DIALOG_USED_LIBRARIES; - /** - * The constant ABOUT_DIALOG_USED_ICONS. - */ public static final String ABOUT_DIALOG_USED_ICONS; - /** - * The constant RESOURCE_PROPERTY_EDIT_CONTROL_NOTHING_IS_SELECTED. - */ public static final String RESOURCE_PROPERTY_EDIT_CONTROL_NOTHING_IS_SELECTED; - /** - * The constant PLUGINS_DIALOG_TITLE. - */ public static final String PLUGINS_DIALOG_TITLE; - /** - * The constant PLUGINS_DIALOG_FILE_CHOOSER_TITLE. - */ public static final String PLUGINS_DIALOG_FILE_CHOOSER_TITLE; - /** - * The constant PLUGINS_DIALOG_FILE_CHOOSER_FILTER. - */ public static final String PLUGINS_DIALOG_FILE_CHOOSER_FILTER; - /** - * The constant PLUGINS_DIALOG_QUESTION. - */ public static final String PLUGINS_DIALOG_QUESTION; static { diff --git a/src/main/java/com/ss/editor/UpdateBSS.java b/src/main/java/com/ss/editor/UpdateBSS.java deleted file mode 100644 index 63e13b82..00000000 --- a/src/main/java/com/ss/editor/UpdateBSS.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.ss.editor; - -import com.sun.javafx.css.Stylesheet; - -import java.io.File; -import java.io.IOException; - -/** - * The class to update bss files. - */ -public class UpdateBSS { - - /** - * The entry point of application. - * - * @param args the input arguments - * @throws IOException the io exception - */ - public static void main(String[] args) throws IOException { - Stylesheet.convertToBinary(new File("./src/main/resources/ui/css/base.css"), new File("./src/main/resources/ui/css/base.bss")); - Stylesheet.convertToBinary(new File("./src/main/resources/ui/css/shadow-color.css"), new File("./src/main/resources/ui/css/shadow-color.bss")); - Stylesheet.convertToBinary(new File("./src/main/resources/ui/css/dark-color.css"), new File("./src/main/resources/ui/css/dark-color.bss")); - Stylesheet.convertToBinary(new File("./src/main/resources/ui/css/light-color.css"), new File("./src/main/resources/ui/css/light-color.bss")); - Stylesheet.convertToBinary(new File("./src/main/resources/ui/css/external.css"), new File("./src/main/resources/ui/css/external.bss")); - Stylesheet.convertToBinary(new File("./src/main/resources/ui/css/custom_ids.css"), new File("./src/main/resources/ui/css/custom_ids.bss")); - Stylesheet.convertToBinary(new File("./src/main/resources/ui/css/custom_classes.css"), new File("./src/main/resources/ui/css/custom_classes.bss")); - } -} diff --git a/src/main/java/com/ss/editor/ui/Icons.java b/src/main/java/com/ss/editor/ui/Icons.java index 2aa64ad6..728cccaa 100644 --- a/src/main/java/com/ss/editor/ui/Icons.java +++ b/src/main/java/com/ss/editor/ui/Icons.java @@ -11,388 +11,105 @@ */ public interface Icons { - /** - * The constant ICON_MANAGER. - */ - @NotNull - FileIconManager ICON_MANAGER = FileIconManager.getInstance(); + @NotNull FileIconManager ICON_MANAGER = FileIconManager.getInstance(); - /** - * The constant REMOVE_12. - */ - Image REMOVE_12 = ICON_MANAGER.getImage("/ui/icons/svg/horizontal-line-remove-button.svg", 12, false); - /** - * The constant ADD_12. - */ - Image ADD_12 = ICON_MANAGER.getImage("/ui/icons/svg/add-plus-button.svg", 12, false); + @NotNull Image REMOVE_12 = ICON_MANAGER.getImage("/ui/icons/svg/horizontal-line-remove-button.svg", 12, false); + @NotNull Image ADD_12 = ICON_MANAGER.getImage("/ui/icons/svg/add-plus-button.svg", 12, false); - /** - * The constant SAVE_16. - */ - Image SAVE_16 = ICON_MANAGER.getImage("/ui/icons/svg/save-disk.svg", 16); - /** - * The constant SCALE_16. - */ - Image SCALE_16 = ICON_MANAGER.getImage("/ui/icons/svg/resize.svg", 16); - /** - * The constant ROTATION_16. - */ - Image ROTATION_16 = ICON_MANAGER.getImage("/ui/icons/svg/eliptical-arrows.svg", 16); - /** - * The constant CUBE_16. - */ - Image CUBE_16 = ICON_MANAGER.getImage("/ui/icons/svg/hollow-cube.svg"); - /** - * The constant MOVE_16. - */ - Image MOVE_16 = ICON_MANAGER.getImage("/ui/icons/svg/move-arrows.svg", 16); - /** - * The constant LIGHT_16. - */ - Image LIGHT_16 = ICON_MANAGER.getImage("/ui/icons/svg/idea.svg"); - /** - * The constant INFLUENCER_16. - */ - Image INFLUENCER_16 = ICON_MANAGER.getImage("/ui/icons/svg/enhance-effect.svg", 16); - /** - * The constant SPHERE_16. - */ - Image SPHERE_16 = ICON_MANAGER.getImage("/ui/icons/svg/planet-sphere.svg"); - /** - * The constant PLANE_16. - */ - Image PLANE_16 = ICON_MANAGER.getImage("/ui/icons/svg/table.svg", 16); - /** - * The constant NODE_16. - */ - Image NODE_16 = ICON_MANAGER.getImage("/ui/icons/svg/family-tree.svg"); - /** - * The constant PARTICLES_16. - */ - Image PARTICLES_16 = ICON_MANAGER.getImage("/ui/icons/svg/molecule_2.svg", 16); - /** - * The DEBUG_16 PARTICLES_16. - */ - Image DEBUG_16 = ICON_MANAGER.getImage("/ui/icons/svg/debug.svg", 16); - /** - * The constant GEOMETRY_16. - */ - Image GEOMETRY_16 = ICON_MANAGER.getImage("/ui/icons/svg/cube-divisions.svg"); - /** - * The constant MESH_16. - */ - Image MESH_16 = ICON_MANAGER.getImage("/ui/icons/svg/grid.svg"); - /** - * The constant MESH_16. - */ - Image MATERIAL_16 = ICON_MANAGER.getImage("/ui/icons/svg/draws.svg"); - /** - * The constant EDIT_16. - */ - Image EDIT_16 = ICON_MANAGER.getImage("/ui/icons/svg/pencil-edit-button.svg"); - /** - * The constant AMBIENT_16. - */ - Image AMBIENT_16 = ICON_MANAGER.getImage("/ui/icons/svg/brightness.svg"); - /** - * The constant LAMP_16. - */ - Image LAMP_16 = ICON_MANAGER.getImage("/ui/icons/svg/lantern.svg"); - /** - * The constant POINT_LIGHT_16. - */ - Image POINT_LIGHT_16 = ICON_MANAGER.getImage("/ui/icons/svg/light-bulb.svg"); - /** - * The constant SUN_16. - */ - Image SUN_16 = ICON_MANAGER.getImage("/ui/icons/svg/sunny-day.svg"); - /** - * The constant PLAY_16. - */ - Image PLAY_16 = ICON_MANAGER.getImage("/ui/icons/svg/play-button.svg"); - /** - * The constant STOP_16. - */ - Image STOP_16 = ICON_MANAGER.getImage("/ui/icons/svg/stop.svg"); - /** - * The constant PAUSE_16. - */ - Image PAUSE_16 = ICON_MANAGER.getImage("/ui/icons/svg/pause.svg"); - /** - * The constant ANIMATION_16. - */ - Image ANIMATION_16 = ICON_MANAGER.getImage("/ui/icons/svg/movie-symbol-of-video-camera.svg", 16); - /** - * The constant GEAR_16. - */ - Image GEAR_16 = ICON_MANAGER.getImage("/ui/icons/svg/settings.svg", 16); - /** - * The constant EXPORT_16. - */ - Image EXPORT_16 = ICON_MANAGER.getImage("/ui/icons/svg/scale-symbol.svg", 16); - /** - * The constant IMPORT_16. - */ - Image IMPORT_16 = ICON_MANAGER.getImage("/ui/icons/svg/import.svg", 16); - /** - * The constant EXPORT_16. - */ - Image EXPLORER_16 = ICON_MANAGER.getImage("/ui/icons/svg/inbox.svg", 16); - /** - * The constant EXPORT_16. - */ - Image EDIT_2_16 = ICON_MANAGER.getImage("/ui/icons/svg/font-selection-editor.svg", 16); - /** - * The constant BONE_16. - */ - Image BONE_16 = ICON_MANAGER.getImage("/ui/icons/svg/bone.svg", 16); - /** - * The constant AUDIO_16. - */ - Image AUDIO_16 = ICON_MANAGER.getImage("/ui/icons/svg/audio-volume.svg"); - /** - * The constant SETTINGS_16. - */ - Image SETTINGS_16 = ICON_MANAGER.getImage("/ui/icons/svg/settings.svg"); - /** - * The constant PASTE_16. - */ - Image PASTE_16 = ICON_MANAGER.getImage("/ui/icons/svg/clipboard-paste-option.svg"); - /** - * The constant NEW_FILE_16. - */ - Image NEW_FILE_16 = ICON_MANAGER.getImage("/ui/icons/svg/add-new-file.svg", 16); - /** - * The constant CUT_16. - */ - Image CUT_16 = ICON_MANAGER.getImage("/ui/icons/svg/cut-content-button.svg", 16); - /** - * The constant COPY_16. - */ - Image COPY_16 = ICON_MANAGER.getImage("/ui/icons/svg/copy-file.svg", 16); - /** - * The constant TRANSFORMATION_16. - */ - Image TRANSFORMATION_16 = ICON_MANAGER.getImage("/ui/icons/svg/transformation-of-geometric-shapes-from-cube-to-cone-outlines.svg"); - /** - * The constant EXTRACT_16. - */ - Image EXTRACT_16 = ICON_MANAGER.getImage("/ui/icons/svg/extract-image.svg", 16); - /** - * The constant SCENE_16. - */ - Image SCENE_16 = ICON_MANAGER.getImage("/ui/icons/svg/line-segment.svg"); - /** - * The constant LAYERS_16. - */ - Image LAYERS_16 = ICON_MANAGER.getImage("/ui/icons/svg/layers.svg", 16); - /** - * The constant OPEN_FILE_16. - */ - Image OPEN_FILE_16 = ICON_MANAGER.getImage("/ui/icons/svg/open-folder-with-document.svg"); - /** - * The constant EMITTER_16. - */ - Image EMITTER_16 = ICON_MANAGER.getImage("/ui/icons/svg/atom-symbol.svg", 16); - /** - * The constant SKY_16. - */ - Image SKY_16 = ICON_MANAGER.getImage("/ui/icons/svg/cloudy-day-outlined-weather-interface-symbol.svg"); - /** - * The constant INVISIBLE_16. - */ - Image INVISIBLE_16 = ICON_MANAGER.getImage("/ui/icons/svg/invisible.svg", 16); - /** - * The constant VISIBLE_16. - */ - Image VISIBLE_16 = ICON_MANAGER.getImage("/ui/icons/svg/eye-view-interface-symbol.svg"); - /** - * The constant STATIC_RIGID_BODY_16. - */ - Image STATIC_RIGID_BODY_16 = ICON_MANAGER.getImage("/ui/icons/svg/brickwall-.svg"); - /** - * The constant RIGID_BODY_16. - */ - Image RIGID_BODY_16 = ICON_MANAGER.getImage("/ui/icons/svg/soccer-ball.svg"); - /** - * The constant REPLAY_16. - */ - Image REPLAY_16 = ICON_MANAGER.getImage("/ui/icons/svg/replay.svg"); - /** - * The constant CHARACTER_16. - */ - Image CHARACTER_16 = ICON_MANAGER.getImage("/ui/icons/svg/user-silhouette.svg", 16); - /** - * The constant SKELETON_16. - */ - Image SKELETON_16 = ICON_MANAGER.getImage("/ui/icons/svg/bones.svg"); - /** - * The constant VEHICLE_16. - */ - Image VEHICLE_16 = ICON_MANAGER.getImage("/ui/icons/svg/sports-car.svg"); - /** - * The constant ATOM_16. - */ - Image ATOM_16 = ICON_MANAGER.getImage("/ui/icons/svg/molecule.svg", 16); - /** - * The constant PHYSICS_16. - */ - Image PHYSICS_16 = ICON_MANAGER.getImage("/ui/icons/svg/black-hole.svg", 16); - /** - * The constant DOLL_16. - */ - Image DOLL_16 = ICON_MANAGER.getImage("/ui/icons/svg/doll.svg", 16); - /** - * The constant CAPSULE_16. - */ - Image CAPSULE_16 = ICON_MANAGER.getImage("/ui/icons/svg/capsule-black-and-white-variant.svg", 16); - /** - * The constant CONE_16. - */ - Image CONE_16 = ICON_MANAGER.getImage("/ui/icons/svg/cone-geometrical-shape.svg", 16); - /** - * The constant CYLINDER_16. - */ - Image CYLINDER_16 = ICON_MANAGER.getImage("/ui/icons/svg/cylinder.svg"); - /** - * The constant TERRAIN_16. - */ - Image TERRAIN_16 = ICON_MANAGER.getImage("/ui/icons/svg/terrain.svg"); - /** - * The constant WHEEL_16. - */ - Image WHEEL_16 = ICON_MANAGER.getImage("/ui/icons/svg/wheel.svg"); - /** - * The constant TRIANGLE_16. - */ - Image TRIANGLE_16 = ICON_MANAGER.getImage("/ui/icons/svg/triangle.svg"); - /** - * The constant DOME_16. - */ - Image DOME_16 = ICON_MANAGER.getImage("/ui/icons/svg/reichstag-dome.svg"); - /** - * The constant QUAD_16. - */ - Image QUAD_16 = ICON_MANAGER.getImage("/ui/icons/svg/basic-square.svg"); - /** - * The constant RHOMB_16. - */ - Image RHOMB_16 = ICON_MANAGER.getImage("/ui/icons/svg/rhombus.svg"); - /** - * The constant TORUS_16. - */ - Image TORUS_16 = ICON_MANAGER.getImage("/ui/icons/svg/circle.svg"); - /** - * The constant POINTS_16. - */ - Image POINTS_16 = ICON_MANAGER.getImage("/ui/icons/svg/because-mathematical-symbol.svg"); - /** - * The constant IMPOSTOR_16. - */ - Image IMPOSTOR_16 = ICON_MANAGER.getImage("/ui/icons/svg/plus.svg"); - /** - * The constant REMOVE_16. - */ - Image REMOVE_16 = ICON_MANAGER.getImage("/ui/icons/svg/horizontal-line-remove-button.svg", 16, false); - /** - * The constant ADD_16. - */ - Image ADD_16 = ICON_MANAGER.getImage("/ui/icons/svg/add-plus-button.svg", 16, false); - /** - * The constant MOTION_16. - */ - Image MOTION_16 = ICON_MANAGER.getImage("/ui/icons/svg/horse-in-running-motion-silhouette.svg", 16, false); - /** - * The constant PATH_16. - */ - Image PATH_16 = ICON_MANAGER.getImage("/ui/icons/svg/map-location.svg", 16, false); - /** - * The constant WAY_POINT_16. - */ - Image WAY_POINT_16 = ICON_MANAGER.getImage("/ui/icons/svg/placeholder.svg", 16, false); - /** - * The constant VERTEX_16. - */ - Image VERTEX_16 = ICON_MANAGER.getImage("/ui/icons/svg/graphene.svg", 16, false); - /** - * The constant DATA_16. - */ - Image DATA_16 = ICON_MANAGER.getImage("/ui/icons/svg/database.svg", 16, false); - /** - * The constant LINKED_NODE_16. - */ - Image LINKED_NODE_16 = ICON_MANAGER.getImage("/ui/icons/svg/link.svg"); - /** - * The constant LINK_FILE_16. - */ - Image LINK_FILE_16 = ICON_MANAGER.getImage("/ui/icons/svg/link-folder-with-document.svg"); - /** - * The constant STATISTICS_16. - */ - Image STATISTICS_16 = ICON_MANAGER.getImage("/ui/icons/svg/bar-chart.svg"); - /** - * The constant STATISTICS_16. - */ - Image DOR_IN_CIRCLE_16 = ICON_MANAGER.getImage("/ui/icons/svg/dot-and-circle.svg"); - /** - * The constant PLUGIN_16. - */ - Image PLUGIN_16 = ICON_MANAGER.getImage("/ui/icons/svg/plug-silhouette.svg"); + @NotNull Image SAVE_16 = ICON_MANAGER.getImage("/ui/icons/svg/save-disk.svg", 16); + @NotNull Image SCALE_16 = ICON_MANAGER.getImage("/ui/icons/svg/resize.svg", 16); + @NotNull Image ROTATION_16 = ICON_MANAGER.getImage("/ui/icons/svg/eliptical-arrows.svg", 16); + @NotNull Image CUBE_16 = ICON_MANAGER.getImage("/ui/icons/svg/hollow-cube.svg"); + @NotNull Image MOVE_16 = ICON_MANAGER.getImage("/ui/icons/svg/move-arrows.svg", 16); + @NotNull Image LIGHT_16 = ICON_MANAGER.getImage("/ui/icons/svg/idea.svg"); + @NotNull Image INFLUENCER_16 = ICON_MANAGER.getImage("/ui/icons/svg/enhance-effect.svg", 16); + @NotNull Image SPHERE_16 = ICON_MANAGER.getImage("/ui/icons/svg/planet-sphere.svg"); + @NotNull Image PLANE_16 = ICON_MANAGER.getImage("/ui/icons/svg/table.svg", 16); + @NotNull Image NODE_16 = ICON_MANAGER.getImage("/ui/icons/svg/family-tree.svg"); + @NotNull Image PARTICLES_16 = ICON_MANAGER.getImage("/ui/icons/svg/molecule_2.svg", 16); + @NotNull Image DEBUG_16 = ICON_MANAGER.getImage("/ui/icons/svg/debug.svg", 16); + @NotNull Image GEOMETRY_16 = ICON_MANAGER.getImage("/ui/icons/svg/cube-divisions.svg"); + @NotNull Image MESH_16 = ICON_MANAGER.getImage("/ui/icons/svg/grid.svg"); + @NotNull Image MATERIAL_16 = ICON_MANAGER.getImage("/ui/icons/svg/draws.svg"); + @NotNull Image EDIT_16 = ICON_MANAGER.getImage("/ui/icons/svg/pencil-edit-button.svg"); + @NotNull Image AMBIENT_16 = ICON_MANAGER.getImage("/ui/icons/svg/brightness.svg"); + @NotNull Image LAMP_16 = ICON_MANAGER.getImage("/ui/icons/svg/lantern.svg"); + @NotNull Image POINT_LIGHT_16 = ICON_MANAGER.getImage("/ui/icons/svg/light-bulb.svg"); + @NotNull Image SUN_16 = ICON_MANAGER.getImage("/ui/icons/svg/sunny-day.svg"); + @NotNull Image PLAY_16 = ICON_MANAGER.getImage("/ui/icons/svg/play-button.svg"); + @NotNull Image STOP_16 = ICON_MANAGER.getImage("/ui/icons/svg/stop.svg"); + @NotNull Image PAUSE_16 = ICON_MANAGER.getImage("/ui/icons/svg/pause.svg"); + @NotNull Image ANIMATION_16 = ICON_MANAGER.getImage("/ui/icons/svg/movie-symbol-of-video-camera.svg", 16); + @NotNull Image GEAR_16 = ICON_MANAGER.getImage("/ui/icons/svg/settings.svg", 16); + @NotNull Image EXPORT_16 = ICON_MANAGER.getImage("/ui/icons/svg/scale-symbol.svg", 16); + @NotNull Image IMPORT_16 = ICON_MANAGER.getImage("/ui/icons/svg/import.svg", 16); + @NotNull Image EXPLORER_16 = ICON_MANAGER.getImage("/ui/icons/svg/inbox.svg", 16); + @NotNull Image EDIT_2_16 = ICON_MANAGER.getImage("/ui/icons/svg/font-selection-editor.svg", 16); + @NotNull Image BONE_16 = ICON_MANAGER.getImage("/ui/icons/svg/bone.svg", 16); + @NotNull Image AUDIO_16 = ICON_MANAGER.getImage("/ui/icons/svg/audio-volume.svg"); + @NotNull Image SETTINGS_16 = ICON_MANAGER.getImage("/ui/icons/svg/settings.svg"); + @NotNull Image PASTE_16 = ICON_MANAGER.getImage("/ui/icons/svg/clipboard-paste-option.svg"); + @NotNull Image NEW_FILE_16 = ICON_MANAGER.getImage("/ui/icons/svg/add-new-file.svg", 16); + @NotNull Image CUT_16 = ICON_MANAGER.getImage("/ui/icons/svg/cut-content-button.svg", 16); + @NotNull Image COPY_16 = ICON_MANAGER.getImage("/ui/icons/svg/copy-file.svg", 16); + @NotNull Image TRANSFORMATION_16 = ICON_MANAGER.getImage("/ui/icons/svg/transformation-of-geometric-shapes-from-cube-to-cone-outlines.svg"); + @NotNull Image EXTRACT_16 = ICON_MANAGER.getImage("/ui/icons/svg/extract-image.svg", 16); + @NotNull Image SCENE_16 = ICON_MANAGER.getImage("/ui/icons/svg/line-segment.svg"); + @NotNull Image LAYERS_16 = ICON_MANAGER.getImage("/ui/icons/svg/layers.svg", 16); + @NotNull Image OPEN_FILE_16 = ICON_MANAGER.getImage("/ui/icons/svg/open-folder-with-document.svg"); + @NotNull Image EMITTER_16 = ICON_MANAGER.getImage("/ui/icons/svg/atom-symbol.svg", 16); + @NotNull Image SKY_16 = ICON_MANAGER.getImage("/ui/icons/svg/cloudy-day-outlined-weather-interface-symbol.svg"); + @NotNull Image INVISIBLE_16 = ICON_MANAGER.getImage("/ui/icons/svg/invisible.svg", 16); + @NotNull Image VISIBLE_16 = ICON_MANAGER.getImage("/ui/icons/svg/eye-view-interface-symbol.svg"); + @NotNull Image STATIC_RIGID_BODY_16 = ICON_MANAGER.getImage("/ui/icons/svg/brickwall-.svg"); + @NotNull Image RIGID_BODY_16 = ICON_MANAGER.getImage("/ui/icons/svg/soccer-ball.svg"); + @NotNull Image REPLAY_16 = ICON_MANAGER.getImage("/ui/icons/svg/replay.svg"); + @NotNull Image CHARACTER_16 = ICON_MANAGER.getImage("/ui/icons/svg/user-silhouette.svg", 16); + @NotNull Image SKELETON_16 = ICON_MANAGER.getImage("/ui/icons/svg/bones.svg"); + @NotNull Image VEHICLE_16 = ICON_MANAGER.getImage("/ui/icons/svg/sports-car.svg"); + @NotNull Image ATOM_16 = ICON_MANAGER.getImage("/ui/icons/svg/molecule.svg", 16); + @NotNull Image PHYSICS_16 = ICON_MANAGER.getImage("/ui/icons/svg/black-hole.svg", 16); + @NotNull Image DOLL_16 = ICON_MANAGER.getImage("/ui/icons/svg/doll.svg", 16); + @NotNull Image CAPSULE_16 = ICON_MANAGER.getImage("/ui/icons/svg/capsule-black-and-white-variant.svg", 16); + @NotNull Image CONE_16 = ICON_MANAGER.getImage("/ui/icons/svg/cone-geometrical-shape.svg", 16); + @NotNull Image CYLINDER_16 = ICON_MANAGER.getImage("/ui/icons/svg/cylinder.svg"); + @NotNull Image TERRAIN_16 = ICON_MANAGER.getImage("/ui/icons/svg/terrain.svg"); + @NotNull Image WHEEL_16 = ICON_MANAGER.getImage("/ui/icons/svg/wheel.svg"); + @NotNull Image TRIANGLE_16 = ICON_MANAGER.getImage("/ui/icons/svg/triangle.svg"); + @NotNull Image DOME_16 = ICON_MANAGER.getImage("/ui/icons/svg/reichstag-dome.svg"); + @NotNull Image QUAD_16 = ICON_MANAGER.getImage("/ui/icons/svg/basic-square.svg"); + @NotNull Image RHOMB_16 = ICON_MANAGER.getImage("/ui/icons/svg/rhombus.svg"); + @NotNull Image TORUS_16 = ICON_MANAGER.getImage("/ui/icons/svg/circle.svg"); + @NotNull Image POINTS_16 = ICON_MANAGER.getImage("/ui/icons/svg/because-mathematical-symbol.svg"); + @NotNull Image IMPOSTOR_16 = ICON_MANAGER.getImage("/ui/icons/svg/plus.svg"); + @NotNull Image REMOVE_16 = ICON_MANAGER.getImage("/ui/icons/svg/horizontal-line-remove-button.svg", 16, false); + @NotNull Image ADD_16 = ICON_MANAGER.getImage("/ui/icons/svg/add-plus-button.svg", 16, false); + @NotNull Image MOTION_16 = ICON_MANAGER.getImage("/ui/icons/svg/horse-in-running-motion-silhouette.svg", 16, false); + @NotNull Image PATH_16 = ICON_MANAGER.getImage("/ui/icons/svg/map-location.svg", 16, false); + @NotNull Image WAY_POINT_16 = ICON_MANAGER.getImage("/ui/icons/svg/placeholder.svg", 16, false); + @NotNull Image VERTEX_16 = ICON_MANAGER.getImage("/ui/icons/svg/graphene.svg", 16, false); + @NotNull Image DATA_16 = ICON_MANAGER.getImage("/ui/icons/svg/database.svg", 16, false); + @NotNull Image LINKED_NODE_16 = ICON_MANAGER.getImage("/ui/icons/svg/link.svg"); + @NotNull Image LINK_FILE_16 = ICON_MANAGER.getImage("/ui/icons/svg/link-folder-with-document.svg"); + @NotNull Image STATISTICS_16 = ICON_MANAGER.getImage("/ui/icons/svg/bar-chart.svg"); + @NotNull Image DOR_IN_CIRCLE_16 = ICON_MANAGER.getImage("/ui/icons/svg/dot-and-circle.svg"); + @NotNull Image PLUGIN_16 = ICON_MANAGER.getImage("/ui/icons/svg/plug-silhouette.svg"); - /** - * The constant REFRESH_18. - */ - Image REFRESH_18 = ICON_MANAGER.getImage("/ui/icons/svg/refresh-button.svg", 18); - /** - * The constant WARNING_24. - */ - Image WARNING_24 = ICON_MANAGER.getImage("/ui/icons/svg/warning.svg", 24); + @NotNull Image REFRESH_18 = ICON_MANAGER.getImage("/ui/icons/svg/refresh-button.svg", 18); + @NotNull Image WARNING_24 = ICON_MANAGER.getImage("/ui/icons/svg/warning.svg", 24); - /** - * The constant TERRAIN_LEVEL_32. - */ - Image TERRAIN_LEVEL_32 = ICON_MANAGER.getImage("/ui/icons/svg/level_terrain.svg", 32); - /** - * The constant TERRAIN_PAINT_32. - */ - Image TERRAIN_PAINT_32 = ICON_MANAGER.getImage("/ui/icons/svg/paint_terrain.svg", 32); - /** - * The constant TERRAIN_ROUGH_32. - */ - Image TERRAIN_ROUGH_32 = ICON_MANAGER.getImage("/ui/icons/svg/rough_terrain.svg", 32); - /** - * The constant TERRAIN_SLOPE_32. - */ - Image TERRAIN_SLOPE_32 = ICON_MANAGER.getImage("/ui/icons/svg/slope_terrain.svg", 32); - /** - * The constant TERRAIN_SMOOTH_32. - */ - Image TERRAIN_SMOOTH_32 = ICON_MANAGER.getImage("/ui/icons/svg/smooth_terrain.svg", 32); - /** - * The constant TERRAIN_UP_32. - */ - Image TERRAIN_UP_32 = ICON_MANAGER.getImage("/ui/icons/svg/raise_terrain.svg", 32); + @NotNull Image TERRAIN_LEVEL_32 = ICON_MANAGER.getImage("/ui/icons/svg/level_terrain.svg", 32); + @NotNull Image TERRAIN_PAINT_32 = ICON_MANAGER.getImage("/ui/icons/svg/paint_terrain.svg", 32); + @NotNull Image TERRAIN_ROUGH_32 = ICON_MANAGER.getImage("/ui/icons/svg/rough_terrain.svg", 32); + @NotNull Image TERRAIN_SLOPE_32 = ICON_MANAGER.getImage("/ui/icons/svg/slope_terrain.svg", 32); + @NotNull Image TERRAIN_SMOOTH_32 = ICON_MANAGER.getImage("/ui/icons/svg/smooth_terrain.svg", 32); + @NotNull Image TERRAIN_UP_32 = ICON_MANAGER.getImage("/ui/icons/svg/raise_terrain.svg", 32); - /** - * The constant APPLICATION_64. - */ - Image APPLICATION_64 = ICON_MANAGER.getImage("/ui/icons/app/SSEd64.png", 64); + @NotNull Image APPLICATION_64 = ICON_MANAGER.getImage("/ui/icons/app/SSEd64.png", 64); - /** - * The constant PLAY_128. - */ - Image PLAY_128 = ICON_MANAGER.getImage("/ui/icons/svg/play-button.svg", 128); - /** - * The constant PAUSE_128. - */ - Image PAUSE_128 = ICON_MANAGER.getImage("/ui/icons/svg/pause.svg", 128); - /** - * The constant STOP_128. - */ - Image STOP_128 = ICON_MANAGER.getImage("/ui/icons/svg/stop.svg", 128); + @NotNull Image PLAY_128 = ICON_MANAGER.getImage("/ui/icons/svg/play-button.svg", 128); + @NotNull Image PAUSE_128 = ICON_MANAGER.getImage("/ui/icons/svg/pause.svg", 128); + @NotNull Image STOP_128 = ICON_MANAGER.getImage("/ui/icons/svg/stop.svg", 128); - /** - * The constant IMAGE_512. - */ - Image IMAGE_512 = ICON_MANAGER.getImage("/ui/icons/svg/picture.svg", 512); + @NotNull Image IMAGE_512 = ICON_MANAGER.getImage("/ui/icons/svg/picture.svg", 512); } diff --git a/src/main/java/com/ss/editor/ui/control/material/tree/factory/MaterialSettingsTreeNodeFactory.java b/src/main/java/com/ss/editor/ui/control/material/tree/factory/MaterialSettingsTreeNodeFactory.java index efcbd29e..0d2da1c4 100644 --- a/src/main/java/com/ss/editor/ui/control/material/tree/factory/MaterialSettingsTreeNodeFactory.java +++ b/src/main/java/com/ss/editor/ui/control/material/tree/factory/MaterialSettingsTreeNodeFactory.java @@ -1,6 +1,7 @@ package com.ss.editor.ui.control.material.tree.factory; import static com.ss.rlib.util.ClassUtils.unsafeCast; +import com.ss.editor.annotation.FXThread; import com.ss.editor.model.node.material.*; import com.ss.editor.ui.control.material.tree.node.*; import com.ss.editor.ui.control.tree.node.TreeNode; @@ -14,6 +15,7 @@ */ public class MaterialSettingsTreeNodeFactory implements TreeNodeFactory { + @FXThread @Override public > @Nullable V createFor(@Nullable final T element, final long objectId) { diff --git a/src/main/java/com/ss/editor/ui/control/tree/node/TreeNodeFactory.java b/src/main/java/com/ss/editor/ui/control/tree/node/TreeNodeFactory.java index 1b02dbda..824dd75b 100644 --- a/src/main/java/com/ss/editor/ui/control/tree/node/TreeNodeFactory.java +++ b/src/main/java/com/ss/editor/ui/control/tree/node/TreeNodeFactory.java @@ -1,5 +1,6 @@ package com.ss.editor.ui.control.tree.node; +import com.ss.editor.annotation.FXThread; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -19,14 +20,15 @@ public interface TreeNodeFactory extends Comparable { * @param objectId the object id. * @return the tree node. */ - @Nullable - > V createFor(@Nullable final T element, final long objectId); + @FXThread + > @Nullable V createFor(@Nullable final T element, final long objectId); /** * Gets an order of this factory. * * @return the order. */ + @FXThread default int getOrder() { return 0; } diff --git a/src/main/java/com/ss/editor/ui/control/tree/node/impl/AnimationTreeNodeFactory.java b/src/main/java/com/ss/editor/ui/control/tree/node/impl/AnimationTreeNodeFactory.java index 8aa7cbc5..45b64f24 100644 --- a/src/main/java/com/ss/editor/ui/control/tree/node/impl/AnimationTreeNodeFactory.java +++ b/src/main/java/com/ss/editor/ui/control/tree/node/impl/AnimationTreeNodeFactory.java @@ -2,6 +2,7 @@ import static com.ss.rlib.util.ClassUtils.unsafeCast; import com.jme3.animation.*; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.control.model.node.control.anim.*; import com.ss.editor.ui.control.tree.node.TreeNode; import com.ss.editor.ui.control.tree.node.TreeNodeFactory; @@ -15,8 +16,8 @@ public class AnimationTreeNodeFactory implements TreeNodeFactory { @Override - @Nullable - public > V createFor(@Nullable final T element, final long objectId) { + @FXThread + public > @Nullable V createFor(@Nullable final T element, final long objectId) { if (element instanceof Animation) { return unsafeCast(new AnimationTreeNode((Animation) element, objectId)); @@ -36,6 +37,7 @@ public > V createFor(@Nullable final T element, final l } @Override + @FXThread public int getOrder() { return 1; } diff --git a/src/main/java/com/ss/editor/ui/control/tree/node/impl/CollisionTreeNodeFactory.java b/src/main/java/com/ss/editor/ui/control/tree/node/impl/CollisionTreeNodeFactory.java index 1cec4eda..0d1ef3ee 100644 --- a/src/main/java/com/ss/editor/ui/control/tree/node/impl/CollisionTreeNodeFactory.java +++ b/src/main/java/com/ss/editor/ui/control/tree/node/impl/CollisionTreeNodeFactory.java @@ -3,6 +3,7 @@ import static com.ss.rlib.util.ClassUtils.unsafeCast; import com.jme3.bullet.collision.shapes.*; import com.jme3.bullet.collision.shapes.infos.ChildCollisionShape; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.control.model.node.physics.shape.*; import com.ss.editor.ui.control.tree.node.TreeNode; import com.ss.editor.ui.control.tree.node.TreeNodeFactory; @@ -16,8 +17,8 @@ public class CollisionTreeNodeFactory implements TreeNodeFactory { @Override - @Nullable - public > V createFor(@Nullable final T element, final long objectId) { + @FXThread + public > @Nullable V createFor(@Nullable final T element, final long objectId) { if (element instanceof ChildCollisionShape) { return unsafeCast(new ChildCollisionShapeTreeNode((ChildCollisionShape) element, objectId)); diff --git a/src/main/java/com/ss/editor/ui/control/tree/node/impl/ControlTreeNodeFactory.java b/src/main/java/com/ss/editor/ui/control/tree/node/impl/ControlTreeNodeFactory.java index ae8c1ceb..574967de 100644 --- a/src/main/java/com/ss/editor/ui/control/tree/node/impl/ControlTreeNodeFactory.java +++ b/src/main/java/com/ss/editor/ui/control/tree/node/impl/ControlTreeNodeFactory.java @@ -8,6 +8,7 @@ import com.jme3.bullet.control.VehicleControl; import com.jme3.cinematic.events.MotionEvent; import com.jme3.scene.control.Control; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.control.model.node.control.ControlTreeNode; import com.ss.editor.ui.control.model.node.control.SkeletonControlTreeNode; import com.ss.editor.ui.control.model.node.control.motion.MotionEventTreeNode; @@ -27,8 +28,8 @@ public class ControlTreeNodeFactory implements TreeNodeFactory { @Override - @Nullable - public > V createFor(@Nullable final T element, final long objectId) { + @FXThread + public > @Nullable V createFor(@Nullable final T element, final long objectId) { if (element instanceof MotionEvent) { return unsafeCast(new MotionEventTreeNode((MotionEvent) element, objectId)); diff --git a/src/main/java/com/ss/editor/ui/control/tree/node/impl/DefaultParticlesTreeNodeFactory.java b/src/main/java/com/ss/editor/ui/control/tree/node/impl/DefaultParticlesTreeNodeFactory.java index 7bef4e63..107aa7a9 100644 --- a/src/main/java/com/ss/editor/ui/control/tree/node/impl/DefaultParticlesTreeNodeFactory.java +++ b/src/main/java/com/ss/editor/ui/control/tree/node/impl/DefaultParticlesTreeNodeFactory.java @@ -7,6 +7,7 @@ import com.jme3.effect.influencers.ParticleInfluencer; import com.jme3.effect.influencers.RadialParticleInfluencer; import com.jme3.effect.shapes.*; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.control.model.node.spatial.particle.emitter.ParticleEmitterTreeNode; import com.ss.editor.ui.control.model.node.spatial.particle.emitter.influencer.DefaultParticleInfluencerTreeNode; import com.ss.editor.ui.control.model.node.spatial.particle.emitter.influencer.EmptyParticleInfluencerTreeNode; @@ -25,8 +26,8 @@ public class DefaultParticlesTreeNodeFactory implements TreeNodeFactory { @Override - @Nullable - public > V createFor(@Nullable final T element, final long objectId) { + @FXThread + public > @Nullable V createFor(@Nullable final T element, final long objectId) { if (element instanceof ParticleEmitter) { return unsafeCast(new ParticleEmitterTreeNode((ParticleEmitter) element, objectId)); @@ -65,6 +66,7 @@ public > V createFor(@Nullable final T element, final l } @Override + @FXThread public int getOrder() { return 1; } diff --git a/src/main/java/com/ss/editor/ui/control/tree/node/impl/DefaultTreeNodeFactory.java b/src/main/java/com/ss/editor/ui/control/tree/node/impl/DefaultTreeNodeFactory.java index 3cfcd329..708d74a1 100644 --- a/src/main/java/com/ss/editor/ui/control/tree/node/impl/DefaultTreeNodeFactory.java +++ b/src/main/java/com/ss/editor/ui/control/tree/node/impl/DefaultTreeNodeFactory.java @@ -9,6 +9,7 @@ import com.jme3.scene.Node; import com.jme3.terrain.geomipmap.TerrainGrid; import com.jme3.terrain.geomipmap.TerrainQuad; +import com.ss.editor.annotation.FXThread; import com.ss.editor.extension.scene.SceneLayer; import com.ss.editor.extension.scene.SceneNode; import com.ss.editor.ui.control.layer.LayersRoot; @@ -30,8 +31,8 @@ public class DefaultTreeNodeFactory implements TreeNodeFactory { @Override - @Nullable - public > V createFor(@Nullable final T element, final long objectId) { + @FXThread + public > @Nullable V createFor(@Nullable final T element, final long objectId) { if (element instanceof LayersRoot) { return unsafeCast(new LayersRootTreeNode((LayersRoot) element, objectId)); diff --git a/src/main/java/com/ss/editor/ui/control/tree/node/impl/LightTreeNodeFactory.java b/src/main/java/com/ss/editor/ui/control/tree/node/impl/LightTreeNodeFactory.java index 9b3eaf48..0023bb80 100644 --- a/src/main/java/com/ss/editor/ui/control/tree/node/impl/LightTreeNodeFactory.java +++ b/src/main/java/com/ss/editor/ui/control/tree/node/impl/LightTreeNodeFactory.java @@ -2,6 +2,7 @@ import static com.ss.rlib.util.ClassUtils.unsafeCast; import com.jme3.light.*; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.control.model.node.light.*; import com.ss.editor.ui.control.tree.node.TreeNode; import com.ss.editor.ui.control.tree.node.TreeNodeFactory; @@ -15,8 +16,8 @@ public class LightTreeNodeFactory implements TreeNodeFactory { @Override - @Nullable - public > V createFor(@Nullable final T element, final long objectId) { + @FXThread + public > @Nullable V createFor(@Nullable final T element, final long objectId) { if (element instanceof LightProbe) { return unsafeCast(new LightProbeTreeNode((LightProbe) element, objectId)); diff --git a/src/main/java/com/ss/editor/ui/control/tree/node/impl/PrimitiveTreeNodeFactory.java b/src/main/java/com/ss/editor/ui/control/tree/node/impl/PrimitiveTreeNodeFactory.java index 175be35f..2ecd12cc 100644 --- a/src/main/java/com/ss/editor/ui/control/tree/node/impl/PrimitiveTreeNodeFactory.java +++ b/src/main/java/com/ss/editor/ui/control/tree/node/impl/PrimitiveTreeNodeFactory.java @@ -5,6 +5,7 @@ import com.jme3.cinematic.MotionPath; import com.jme3.math.Vector3f; import com.jme3.scene.VertexBuffer; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.control.model.node.BufferTreeNode; import com.ss.editor.ui.control.model.node.PositionTreeNode; import com.ss.editor.ui.control.model.node.VertexBufferTreeNode; @@ -24,8 +25,8 @@ public class PrimitiveTreeNodeFactory implements TreeNodeFactory { @Override - @Nullable - public > V createFor(@Nullable final T element, final long objectId) { + @FXThread + public > @Nullable V createFor(@Nullable final T element, final long objectId) { if (element instanceof Vector3f) { return unsafeCast(new PositionTreeNode((Vector3f) element, objectId)); @@ -43,6 +44,7 @@ public > V createFor(@Nullable final T element, final l } @Override + @FXThread public int getOrder() { return 1; } diff --git a/src/main/java/com/ss/editor/ui/control/tree/node/impl/Toneg0dTreeNodeFactory.java b/src/main/java/com/ss/editor/ui/control/tree/node/impl/Toneg0dTreeNodeFactory.java index a18115ad..7e683acc 100644 --- a/src/main/java/com/ss/editor/ui/control/tree/node/impl/Toneg0dTreeNodeFactory.java +++ b/src/main/java/com/ss/editor/ui/control/tree/node/impl/Toneg0dTreeNodeFactory.java @@ -1,6 +1,7 @@ package com.ss.editor.ui.control.tree.node.impl; import static com.ss.rlib.util.ClassUtils.unsafeCast; +import com.ss.editor.annotation.FXThread; import com.ss.editor.model.node.particles.Toneg0dParticleInfluencers; import com.ss.editor.ui.control.model.node.spatial.particle.emitter.toneg0d.Toneg0DParticleEmitterNodeTreeNode; import com.ss.editor.ui.control.model.node.spatial.particle.emitter.toneg0d.influencer.Toneg0DParticleInfluencerTreeNode; @@ -19,8 +20,8 @@ public class Toneg0dTreeNodeFactory implements TreeNodeFactory { @Override - @Nullable - public > V createFor(@Nullable final T element, final long objectId) { + @FXThread + public > @Nullable V createFor(@Nullable final T element, final long objectId) { if (element instanceof ParticleEmitterNode) { return unsafeCast(new Toneg0DParticleEmitterNodeTreeNode((ParticleEmitterNode) element, objectId)); @@ -34,6 +35,7 @@ public > V createFor(@Nullable final T element, final l } @Override + @FXThread public int getOrder() { return 1; } diff --git a/src/main/java/com/ss/editor/ui/css/CSSClasses.java b/src/main/java/com/ss/editor/ui/css/CSSClasses.java index 31d1ba78..c69d654a 100644 --- a/src/main/java/com/ss/editor/ui/css/CSSClasses.java +++ b/src/main/java/com/ss/editor/ui/css/CSSClasses.java @@ -1,5 +1,7 @@ package com.ss.editor.ui.css; +import org.jetbrains.annotations.NotNull; + /** * The list of custom css classes. * @@ -7,508 +9,160 @@ */ public interface CSSClasses { - /** - * The constant SPECIAL_FONT_13. - */ - String SPECIAL_FONT_13 = "special-font-13"; - /** - * The constant SPECIAL_FONT_14. - */ - String SPECIAL_FONT_14 = "special-font-14"; - /** - * The constant SPECIAL_FONT_16. - */ - String SPECIAL_FONT_16 = "special-font-16"; - /** - * The constant MAIN_SPLIT_PANEL. - */ - String MAIN_SPLIT_PANEL = "main-split-pane"; - - /** - * The constant DEF_HBOX. - */ - String DEF_HBOX = "hbox"; - /** - * The constant DEF_VBOX. - */ - String DEF_VBOX = "vbox"; - /** - * The constant DEF_GRID_PANE. - */ - String DEF_GRID_PANE = "grid-pane"; - /** - * The constant DEF_STACK_PANE. - */ - String DEF_STACK_PANE = "stack-pane"; - /** - * The constant DEF_BORDER_PANE. - */ - String DEF_BORDER_PANE = "border-pane"; - - /** - * The constant DIALOG_ROOT. - */ - String DIALOG_ROOT = "dialog-root"; - /** - * The constant DIALOG_ACTIONS_ROOT. - */ - String DIALOG_ACTIONS_ROOT = "dialog-actions-root"; - /** - * The constant DIALOG_CONTENT_ROOT. - */ - String DIALOG_CONTENT_ROOT = "dialog-content-root"; - /** - * The constant DIALOG_BUTTON. - */ - String DIALOG_BUTTON = "dialog-button"; - /** - * The constant DIALOG_LABEL_WARNING. - */ - String DIALOG_LABEL_WARNING = "dialog-label-warning"; - /** - * The constant DIALOG_DYNAMIC_LABEL. - */ - String DIALOG_DYNAMIC_LABEL = "dialog-dynamic-label"; - /** - * The constant DIALOG_FIELD. - */ - String DIALOG_FIELD = "dialog-field"; - - /** - * The constant TEXT_INPUT_CONTAINER. - */ - String TEXT_INPUT_CONTAINER = "text-input-container"; - - /** - * The constant TEXT_INPUT_CONTAINER_WITHOUT_PADDING. - */ - String TEXT_INPUT_CONTAINER_WITHOUT_PADDING = "text-input-container-without-padding"; - - /** - * The constant CHOOSE_TEXTURE_CONTROL. - */ - String CHOOSE_TEXTURE_CONTROL = "choose-texture-control"; - /** - * The constant CHOOSE_TEXTURE_CONTROL_TEXTURE_LABEL. - */ - String CHOOSE_TEXTURE_CONTROL_TEXTURE_LABEL = "choose-texture-control-texture-label"; - /** - * The constant CHOOSE_TEXTURE_CONTROL_PREVIEW. - */ - String CHOOSE_TEXTURE_CONTROL_PREVIEW = "choose-texture-control-preview"; - - /** - * The constant CHOOSE_RESOURCE_CONTROL. - */ - String CHOOSE_RESOURCE_CONTROL = "choose-resource-control"; - - /** - * The constant TEXT_FIELD_IN_COMBO_BOX. - */ - String TEXT_FIELD_IN_COMBO_BOX = "text-field-in-combo-box"; - /** - * The constant TRANSPARENT_SPINNER. - */ - String TRANSPARENT_SPINNER = "transparent-spinner"; - /** - * The constant TRANSPARENT_TEXT_FIELD. - */ - String TRANSPARENT_TEXT_FIELD = "transparent-text-field"; - /** - * The constant TRANSPARENT_TREE_VIEW. - */ - String TRANSPARENT_TREE_VIEW = "transparent-tree-view"; - /** - * The constant TRANSPARENT_LIST_VIEW. - */ - String TRANSPARENT_LIST_VIEW = "transparent-list-view"; - /** - * The constant TRANSPARENT_LIST_CELL. - */ - String TRANSPARENT_LIST_CELL = "transparent-list-cell"; - /** - * The constant TRANSPARENT_COMBO_BOX. - */ - String TRANSPARENT_COMBO_BOX = "transparent-combo-box"; - /** - * The constant TRANSPARENT_TEXT_AREA. - */ - String TRANSPARENT_TEXT_AREA = "transparent-text-area"; - - /** - * The constant BUTTON_WITHOUT_LEFT_BORDER. - */ - String BUTTON_WITHOUT_LEFT_BORDER = "button-without-left-border"; - /** - * The constant BUTTON_WITHOUT_LEFT_RIGHT_BORDER. - */ - String BUTTON_WITHOUT_LEFT_RIGHT_BORDER = "button-without-left-right-border"; - /** - * The constant BUTTON_WITHOUT_RIGHT_BORDER. - */ - String BUTTON_WITHOUT_RIGHT_BORDER = "button-without-right-border"; - - /** - * The constant PROCESSING_COMPONENT_CONTAINER. - */ - String PROCESSING_COMPONENT_CONTAINER = "processing-component-container"; - /** - * The constant PROCESSING_COMPONENT_TERRAIN_EDITOR. - */ - String PROCESSING_COMPONENT_TERRAIN_EDITOR = "processing-component-terrain-editor"; - /** - * The constant PROCESSING_COMPONENT_TERRAIN_EDITOR_TEXTURE_LAYER. - */ - String PROCESSING_COMPONENT_TERRAIN_EDITOR_TEXTURE_LAYER = "processing-component-terrain-layer"; - /** - * The constant TERRAIN_EDITING_TEXTURE_LAYERS_SETTINGS_BUTTONS. - */ - String PROCESSING_COMPONENT_TERRAIN_EDITOR_LAYERS_SETTINGS_BUTTONS = "processing-component-terrain-texture-layers-settings-buttons"; - - /** - * The constant LIST_VIEW_WITHOUT_SCROLL. - */ - String LIST_VIEW_WITHOUT_SCROLL = "list-view-without-scroll"; - - /** - * The constant FLAT_BUTTON. - */ - String FLAT_BUTTON = "flat-button"; - /** - * The constant INPUT_CONTROL_TOOLBAR_BUTTON. - */ - String INPUT_CONTROL_TOOLBAR_BUTTON = "input-control-toolbar-button"; - /** - * The constant FILE_EDITOR_TOOLBAR_BUTTON. - */ - String FILE_EDITOR_TOOLBAR_BUTTON = "file-editor-toolbar-button"; - /** - * The constant FILE_EDITOR_TOOLBAR_LABEL. - */ - String FILE_EDITOR_TOOLBAR_LABEL = "file-editor-toolbar-label"; - /** - * The constant FILE_EDITOR_TOOLBAR_FIELD. - */ - String FILE_EDITOR_TOOLBAR_FIELD = "file-editor-toolbar-field"; - - /** - * The constant FILE_EDITOR_TOOLBAR. - */ - String FILE_EDITOR_TOOLBAR = "file-editor-toolbar"; - /** - * The constant FILE_EDITOR_EDITOR_AREA. - */ - String FILE_EDITOR_EDITOR_AREA = "file-editor-editor-area"; - /** - * The constant FILE_EDITOR_MAIN_SPLIT_PANE. - */ - String FILE_EDITOR_MAIN_SPLIT_PANE = "file-editor-main-split-pane"; - /** - * The constant FILE_EDITOR_TOOL_SPLIT_PANE. - */ - String FILE_EDITOR_TOOL_SPLIT_PANE = "file-editor-tool-split-pane"; - /** - * The constant FILE_EDITOR_TOOL_COMPONENT. - */ - String FILE_EDITOR_TOOL_COMPONENT = "file-editor-tool-component"; - - /** - * The constant MEDIUM_TOGGLE_BUTTON. - */ - String MEDIUM_TOGGLE_BUTTON = "medium-toggle-button"; - - /** - * The constant MATERIAL_FILE_EDITOR_PROPERTIES_COMPONENT. - */ - String MATERIAL_FILE_EDITOR_PROPERTIES_COMPONENT = "material-file-editor-properties-component"; - /** - * The constant MATERIAL_FILE_EDITOR_PARAM_CONTROL. - */ - String MATERIAL_FILE_EDITOR_PARAM_CONTROL = "material-file-editor-param-control"; - /** - * The constant MATERIAL_FILE_EDITOR_PARAM_CONTROL_NAME. - */ - String MATERIAL_FILE_EDITOR_PARAM_CONTROL_NAME = "material-file-editor-param-control-name"; - /** - * The constant MATERIAL_FILE_EDITOR_PARAM_CONTROL_TEXTURE_PREVIEW. - */ - String MATERIAL_FILE_EDITOR_PARAM_CONTROL_TEXTURE_PREVIEW = "material-file-editor-param-control-texture-preview"; - /** - * The constant MATERIAL_FILE_EDITOR_PARAM_CONTROL_BUTTON. - */ - String MATERIAL_FILE_EDITOR_PARAM_CONTROL_BUTTON = "material-file-editor-param-control-button"; - /** - * The constant MATERIAL_FILE_EDITOR_PARAM_CONTROL_COLOR_PICKER. - */ - String MATERIAL_FILE_EDITOR_PARAM_CONTROL_COLOR_PICKER = "material-file-editor-param-control-color-picker"; - /** - * The constant MATERIAL_FILE_EDITOR_PARAM_CONTROL_CHECKBOX. - */ - String MATERIAL_FILE_EDITOR_PARAM_CONTROL_CHECKBOX = "material-file-editor-param-control-checkbox"; - /** - * The constant MATERIAL_FILE_EDITOR_PARAM_CONTROL_SPINNER. - */ - String MATERIAL_FILE_EDITOR_PARAM_CONTROL_SPINNER = "MaterialParamControlSpinner"; - /** - * The constant MATERIAL_FILE_EDITOR_PARAM_CONTROL_COMBO_BOX. - */ - String MATERIAL_FILE_EDITOR_PARAM_CONTROL_COMBO_BOX = "material-file-editor-param-control-combo-box"; - - /** - * The constant SCENE_EDITOR_STATS_CONTAINER. - */ - String SCENE_EDITOR_STATS_CONTAINER = "scene-editor-stats-container"; - - /** - * The constant ABSTRACT_NODE_TREE_CONTAINER. - */ - String ABSTRACT_NODE_TREE_CONTAINER = "abstract-node-tree-container"; - /** - * The constant ABSTRACT_NODE_TREE_CELL. - */ - String ABSTRACT_NODE_TREE_CELL = "abstract-node-tree-cell"; - - /** - * The constant ABSTRACT_PARAM_CONTROL_CONTAINER. - */ - String ABSTRACT_PARAM_CONTROL_CONTAINER = "abstract-param-control-container"; - /** - * The constant ABSTRACT_PARAM_CONTROL_CONTAINER_SPLIT_LINE. - */ - String ABSTRACT_PARAM_CONTROL_CONTAINER_SPLIT_LINE = "abstract-param-control-container-split-line"; - /** - * The constant ABSTRACT_PARAM_CONTROL. - */ - String ABSTRACT_PARAM_CONTROL = "abstract-param-control"; - /** - * The constant ABSTRACT_PARAM_EDITOR_CONTROL. - */ - String ABSTRACT_PARAM_EDITOR_CONTROL = "abstract-param-editor-control"; - /** - * The constant ABSTRACT_PARAM_CONTROL_PARAM_NAME_SINGLE_ROW. - */ - String ABSTRACT_PARAM_CONTROL_PARAM_NAME_SINGLE_ROW = "abstract-param-control-param-name-single-row"; - /** - * The constant ABSTRACT_PARAM_CONTROL_PARAM_NAME. - */ - String ABSTRACT_PARAM_CONTROL_PARAM_NAME = "abstract-param-control-param-name"; - /** - * The constant ABSTRACT_PARAM_CONTROL_ELEMENT_LABEL. - */ - String ABSTRACT_PARAM_CONTROL_ELEMENT_LABEL = "abstract-param-control-element-label"; - /** - * The constant ABSTRACT_PARAM_CONTROL_PREVIEW_CONTAINER. - */ - String ABSTRACT_PARAM_CONTROL_PREVIEW_CONTAINER = "abstract-param-control-preview-container"; - /** - * The constant ABSTRACT_PARAM_CONTROL_INPUT_CONTAINER. - */ - String ABSTRACT_PARAM_CONTROL_INPUT_CONTAINER = "abstract-param-control-input-container"; - /** - * The constant ABSTRACT_PARAM_CONTROL_SHORT_INPUT_CONTAINER. - */ - String ABSTRACT_PARAM_CONTROL_SHORT_INPUT_CONTAINER = "abstract-param-control-short-input-container"; - /** - * The constant ABSTRACT_PARAM_CONTROL_CHECK_BOX. - */ - String ABSTRACT_PARAM_CONTROL_CHECK_BOX = "abstract-param-control-checkbox"; - /** - * The constant ABSTRACT_PARAM_CONTROL_COLOR_PICKER. - */ - String ABSTRACT_PARAM_CONTROL_COLOR_PICKER = "abstract-param-control-param-color-picker"; - /** - * The constant ABSTRACT_PARAM_CONTROL_COMBO_BOX. - */ - String ABSTRACT_PARAM_CONTROL_COMBO_BOX = "abstract-param-control-combobox"; - /** - * The constant ABSTRACT_PARAM_CONTROL_VECTOR2F_FIELD. - */ - String ABSTRACT_PARAM_CONTROL_VECTOR2F_FIELD = "abstract-param-control-vector2f-field"; - /** - * The constant ABSTRACT_PARAM_CONTROL_VECTOR3F_FIELD. - */ - String ABSTRACT_PARAM_CONTROL_VECTOR3F_FIELD = "abstract-param-control-vector3f-field"; - /** - * The constant ABSTRACT_PARAM_CONTROL_LABEL_VALUE. - */ - String ABSTRACT_PARAM_CONTROL_LABEL_VALUE = "abstract-param-control-label-value"; - /** - * The constant ABSTRACT_PARAM_CONTROL_NUMBER_LABEL. - */ - String ABSTRACT_PARAM_CONTROL_NUMBER_LABEL = "abstract-param-control-number-label"; - /** - * The constant ABSTRACT_PARAM_CONTROL_INFLUENCER. - */ - String ABSTRACT_PARAM_CONTROL_INFLUENCER = "abstract-param-control-influencer"; - /** - * The constant ABSTRACT_PARAM_CONTROL_INFLUENCER_ELEMENT. - */ - String ABSTRACT_PARAM_CONTROL_INFLUENCER_ELEMENT = "abstract-param-control-influencer-element"; - /** - * The constant ABSTRACT_RESOURCE_PROPERTY_CONTROL. - */ - String ABSTRACT_RESOURCE_PROPERTY_CONTROL = "abstract-resource-property-control"; - - /** - * The constant EDITOR_SCRIPTING_COMPONENT. - */ - String EDITOR_SCRIPTING_COMPONENT = "editor-scripting-component"; - /** - * The constant GROOVY_EDITOR_COMPONENT. - */ - String GROOVY_EDITOR_COMPONENT = "groovy-editor-component"; - - /** - * The constant SCENE_APP_STATE_CONTAINER. - */ - String SCENE_APP_STATE_CONTAINER = "scene-app-state-container"; - /** - * The constant SCENE_APP_STATE_LIST_CELL. - */ - String SCENE_APP_STATE_LIST_CELL = "scene-app-state-list-cell"; - - /** - * The constant SCENE_FILTER_CONTAINER. - */ - String SCENE_FILTER_CONTAINER = "scene-filter-container"; - /** - * The constant SCENE_FILTER_LIST_CELL. - */ - String SCENE_FILTER_LIST_CELL = "scene-filter-list-cell"; - - /** - * The constant SETTINGS_DIALOG. - */ - String SETTINGS_DIALOG = "settings-dialog"; - /** - * The constant SETTINGS_DIALOG_LABEL. - */ - String SETTINGS_DIALOG_LABEL = "settings-dialog-label"; - /** - * The constant SETTINGS_DIALOG_FIELD. - */ - String SETTINGS_DIALOG_FIELD = "settings-dialog-field"; - /** - * The constant SETTINGS_DIALOG_SHORT_LABEL. - */ - String SETTINGS_DIALOG_SHORT_LABEL = "settings-dialog-short-label"; - /** - * The constant SETTINGS_DIALOG_MESSAGE_LABEL. - */ - String SETTINGS_DIALOG_MESSAGE_LABEL = "settings-dialog-message-label"; - - /** - * The constant CONFIRM_DIALOG. - */ - String CONFIRM_DIALOG = "confirm-dialog"; - - /** - * The constant CREATE_SKY_DIALOG. - */ - String CREATE_SKY_DIALOG = "create-sky-dialog"; - - /** - * The constant ABOUT_DIALOG. - */ - String ABOUT_DIALOG = "about-dialog"; - /** - * The constant ABOUT_DIALOG_LONG_LABEL. - */ - String ABOUT_DIALOG_LONG_LABEL = "about-dialog-long-label"; - /** - * The constant ABOUT_DIALOG_TITLE_LABEL. - */ - String ABOUT_DIALOG_TITLE_LABEL = "about-dialog-title-label"; - - /** - * The constant FILE_CREATOR_DIALOG. - */ - String FILE_CREATOR_DIALOG = "file-creator-dialog"; - - /** - * The constant SAVE_AS_DIALOG. - */ - String SAVE_AS_DIALOG = "save-as-dialog"; - - /** - * The constant GENERATE_LOD_DIALOG. - */ - String GENERATE_LOD_DIALOG = "generate-lod-dialog"; - /** - * The constant PLUGINS_DIALOG. - */ - String PLUGINS_DIALOG = "plugins-dialog"; - - /** - * The constant CREATE_TERRAIN_DIALOG. - */ - String CREATE_TERRAIN_DIALOG = "create-terrain-dialog"; - - /** - * The constant NODE_SELECTOR_DIALOG. - */ - String NODE_SELECTOR_DIALOG = "node-selector-dialog"; - - /** - * The constant ASSET_EDITOR_DIALOG. - */ - String ASSET_EDITOR_DIALOG = "asset-editor-dialog"; - /** - * The constant ASSET_EDITOR_DIALOG_ACTIONS. - */ - String ASSET_EDITOR_DIALOG_ACTIONS = "asset-editor-dialog-actions"; - /** - * The constant ASSET_EDITOR_DIALOG_PREVIEW_CONTAINER. - */ - String ASSET_EDITOR_DIALOG_PREVIEW_CONTAINER = "asset-editor-dialog-preview-container"; - - /** - * The constant OPEN_EXTERNAL_FOLDER_EDITOR_DIALOG. - */ - String OPEN_EXTERNAL_FOLDER_EDITOR_DIALOG = "open-external-folder-editor-dialog"; - - /** - * The constant AUDIO_VIEW_EDITOR_CONTAINER. - */ - String AUDIO_VIEW_EDITOR_CONTAINER = "audio-view-editor-container"; - - /** - * The constant TEXT_EDITOR_TEXT_AREA. - */ - String TEXT_EDITOR_TEXT_AREA = "text-editor-text-area"; - - /** - * The constant IMAGE_VIEW_EDITOR_CONTAINER. - */ - String IMAGE_VIEW_EDITOR_CONTAINER = "image-view-editor-container"; - - /** - * The constant PHYSICS_NODE_LIST_CONTROL. - */ - String PHYSICS_NODE_LIST_CONTROL = "physics-node-list-control"; - - /** - * The constant CUSTOM_TOOLTIP. - */ - String CUSTOM_TOOLTIP = "custom-tooltip"; - - /** - * The constant IMAGE_CHANNEL_PREVIEW. - */ - String IMAGE_CHANNEL_PREVIEW = "image-channel-preview"; - - /** - * The constant IMAGE_PREVIEW. - */ - String IMAGE_PREVIEW = "image-preview"; - - /** - * The constant PLUGIN_LIST_CELL. - */ - String PLUGIN_LIST_CELL = "plugin-list-cell"; - - /** - * The constant STATS_3D_STATE. - */ - String STATS_3D_STATE = "stats-3dstate"; + @NotNull String SPECIAL_FONT_13 = "special-font-13"; + @NotNull String SPECIAL_FONT_14 = "special-font-14"; + @NotNull String SPECIAL_FONT_16 = "special-font-16"; + @NotNull String MAIN_SPLIT_PANEL = "main-split-pane"; + + @NotNull String DEF_HBOX = "hbox"; + @NotNull String DEF_VBOX = "vbox"; + @NotNull String DEF_GRID_PANE = "grid-pane"; + @NotNull String DEF_STACK_PANE = "stack-pane"; + @NotNull String DEF_BORDER_PANE = "border-pane"; + + @NotNull String DIALOG_ROOT = "dialog-root"; + @NotNull String DIALOG_ACTIONS_ROOT = "dialog-actions-root"; + @NotNull String DIALOG_CONTENT_ROOT = "dialog-content-root"; + @NotNull String DIALOG_BUTTON = "dialog-button"; + @NotNull String DIALOG_LABEL_WARNING = "dialog-label-warning"; + @NotNull String DIALOG_DYNAMIC_LABEL = "dialog-dynamic-label"; + @NotNull String DIALOG_FIELD = "dialog-field"; + + @NotNull String TEXT_INPUT_CONTAINER = "text-input-container"; + + @NotNull String TEXT_INPUT_CONTAINER_WITHOUT_PADDING = "text-input-container-without-padding"; + + @NotNull String CHOOSE_TEXTURE_CONTROL = "choose-texture-control"; + @NotNull String CHOOSE_TEXTURE_CONTROL_TEXTURE_LABEL = "choose-texture-control-texture-label"; + @NotNull String CHOOSE_TEXTURE_CONTROL_PREVIEW = "choose-texture-control-preview"; + + @NotNull String CHOOSE_RESOURCE_CONTROL = "choose-resource-control"; + + @NotNull String TEXT_FIELD_IN_COMBO_BOX = "text-field-in-combo-box"; + @NotNull String TRANSPARENT_SPINNER = "transparent-spinner"; + @NotNull String TRANSPARENT_TEXT_FIELD = "transparent-text-field"; + @NotNull String TRANSPARENT_TREE_VIEW = "transparent-tree-view"; + @NotNull String TRANSPARENT_LIST_VIEW = "transparent-list-view"; + @NotNull String TRANSPARENT_LIST_CELL = "transparent-list-cell"; + @NotNull String TRANSPARENT_COMBO_BOX = "transparent-combo-box"; + @NotNull String TRANSPARENT_TEXT_AREA = "transparent-text-area"; + + @NotNull String BUTTON_WITHOUT_LEFT_BORDER = "button-without-left-border"; + @NotNull String BUTTON_WITHOUT_LEFT_RIGHT_BORDER = "button-without-left-right-border"; + @NotNull String BUTTON_WITHOUT_RIGHT_BORDER = "button-without-right-border"; + + @NotNull String PROCESSING_COMPONENT_CONTAINER = "processing-component-container"; + @NotNull String PROCESSING_COMPONENT_TERRAIN_EDITOR = "processing-component-terrain-editor"; + @NotNull String PROCESSING_COMPONENT_TERRAIN_EDITOR_TEXTURE_LAYER = "processing-component-terrain-layer"; + @NotNull String PROCESSING_COMPONENT_TERRAIN_EDITOR_LAYERS_SETTINGS_BUTTONS = "processing-component-terrain-texture-layers-settings-buttons"; + + @NotNull String LIST_VIEW_WITHOUT_SCROLL = "list-view-without-scroll"; + + @NotNull String FLAT_BUTTON = "flat-button"; + @NotNull String INPUT_CONTROL_TOOLBAR_BUTTON = "input-control-toolbar-button"; + @NotNull String FILE_EDITOR_TOOLBAR_BUTTON = "file-editor-toolbar-button"; + @NotNull String FILE_EDITOR_TOOLBAR_LABEL = "file-editor-toolbar-label"; + @NotNull String FILE_EDITOR_TOOLBAR_FIELD = "file-editor-toolbar-field"; + + @NotNull String FILE_EDITOR_TOOLBAR = "file-editor-toolbar"; + @NotNull String FILE_EDITOR_EDITOR_AREA = "file-editor-editor-area"; + @NotNull String FILE_EDITOR_MAIN_SPLIT_PANE = "file-editor-main-split-pane"; + @NotNull String FILE_EDITOR_TOOL_SPLIT_PANE = "file-editor-tool-split-pane"; + @NotNull String FILE_EDITOR_TOOL_COMPONENT = "file-editor-tool-component"; + + @NotNull String MEDIUM_TOGGLE_BUTTON = "medium-toggle-button"; + + @NotNull String MATERIAL_FILE_EDITOR_PROPERTIES_COMPONENT = "material-file-editor-properties-component"; + @NotNull String MATERIAL_FILE_EDITOR_PARAM_CONTROL = "material-file-editor-param-control"; + @NotNull String MATERIAL_FILE_EDITOR_PARAM_CONTROL_NAME = "material-file-editor-param-control-name"; + @NotNull String MATERIAL_FILE_EDITOR_PARAM_CONTROL_TEXTURE_PREVIEW = "material-file-editor-param-control-texture-preview"; + @NotNull String MATERIAL_FILE_EDITOR_PARAM_CONTROL_BUTTON = "material-file-editor-param-control-button"; + @NotNull String MATERIAL_FILE_EDITOR_PARAM_CONTROL_COLOR_PICKER = "material-file-editor-param-control-color-picker"; + @NotNull String MATERIAL_FILE_EDITOR_PARAM_CONTROL_CHECKBOX = "material-file-editor-param-control-checkbox"; + @NotNull String MATERIAL_FILE_EDITOR_PARAM_CONTROL_SPINNER = "MaterialParamControlSpinner"; + @NotNull String MATERIAL_FILE_EDITOR_PARAM_CONTROL_COMBO_BOX = "material-file-editor-param-control-combo-box"; + + @NotNull String SCENE_EDITOR_STATS_CONTAINER = "scene-editor-stats-container"; + + @NotNull String ABSTRACT_NODE_TREE_CONTAINER = "abstract-node-tree-container"; + @NotNull String ABSTRACT_NODE_TREE_CELL = "abstract-node-tree-cell"; + + @NotNull String ABSTRACT_PARAM_CONTROL_CONTAINER = "abstract-param-control-container"; + @NotNull String ABSTRACT_PARAM_CONTROL_CONTAINER_SPLIT_LINE = "abstract-param-control-container-split-line"; + @NotNull String ABSTRACT_PARAM_CONTROL = "abstract-param-control"; + @NotNull String ABSTRACT_PARAM_EDITOR_CONTROL = "abstract-param-editor-control"; + @NotNull String ABSTRACT_PARAM_CONTROL_PARAM_NAME_SINGLE_ROW = "abstract-param-control-param-name-single-row"; + @NotNull String ABSTRACT_PARAM_CONTROL_PARAM_NAME = "abstract-param-control-param-name"; + @NotNull String ABSTRACT_PARAM_CONTROL_ELEMENT_LABEL = "abstract-param-control-element-label"; + @NotNull String ABSTRACT_PARAM_CONTROL_PREVIEW_CONTAINER = "abstract-param-control-preview-container"; + @NotNull String ABSTRACT_PARAM_CONTROL_INPUT_CONTAINER = "abstract-param-control-input-container"; + @NotNull String ABSTRACT_PARAM_CONTROL_SHORT_INPUT_CONTAINER = "abstract-param-control-short-input-container"; + @NotNull String ABSTRACT_PARAM_CONTROL_CHECK_BOX = "abstract-param-control-checkbox"; + @NotNull String ABSTRACT_PARAM_CONTROL_COLOR_PICKER = "abstract-param-control-param-color-picker"; + @NotNull String ABSTRACT_PARAM_CONTROL_COMBO_BOX = "abstract-param-control-combobox"; + @NotNull String ABSTRACT_PARAM_CONTROL_VECTOR2F_FIELD = "abstract-param-control-vector2f-field"; + @NotNull String ABSTRACT_PARAM_CONTROL_VECTOR3F_FIELD = "abstract-param-control-vector3f-field"; + @NotNull String ABSTRACT_PARAM_CONTROL_LABEL_VALUE = "abstract-param-control-label-value"; + @NotNull String ABSTRACT_PARAM_CONTROL_NUMBER_LABEL = "abstract-param-control-number-label"; + @NotNull String ABSTRACT_PARAM_CONTROL_INFLUENCER = "abstract-param-control-influencer"; + @NotNull String ABSTRACT_PARAM_CONTROL_INFLUENCER_ELEMENT = "abstract-param-control-influencer-element"; + @NotNull String ABSTRACT_RESOURCE_PROPERTY_CONTROL = "abstract-resource-property-control"; + + @NotNull String EDITOR_SCRIPTING_COMPONENT = "editor-scripting-component"; + @NotNull String GROOVY_EDITOR_COMPONENT = "groovy-editor-component"; + + @NotNull String SCENE_APP_STATE_CONTAINER = "scene-app-state-container"; + @NotNull String SCENE_APP_STATE_LIST_CELL = "scene-app-state-list-cell"; + + @NotNull String SCENE_FILTER_CONTAINER = "scene-filter-container"; + @NotNull String SCENE_FILTER_LIST_CELL = "scene-filter-list-cell"; + + @NotNull String SETTINGS_DIALOG = "settings-dialog"; + @NotNull String SETTINGS_DIALOG_LABEL = "settings-dialog-label"; + @NotNull String SETTINGS_DIALOG_FIELD = "settings-dialog-field"; + @NotNull String SETTINGS_DIALOG_SHORT_LABEL = "settings-dialog-short-label"; + @NotNull String SETTINGS_DIALOG_MESSAGE_LABEL = "settings-dialog-message-label"; + + @NotNull String CONFIRM_DIALOG = "confirm-dialog"; + + @NotNull String CREATE_SKY_DIALOG = "create-sky-dialog"; + + @NotNull String ABOUT_DIALOG = "about-dialog"; + @NotNull String ABOUT_DIALOG_LONG_LABEL = "about-dialog-long-label"; + @NotNull String ABOUT_DIALOG_TITLE_LABEL = "about-dialog-title-label"; + + @NotNull String FILE_CREATOR_DIALOG = "file-creator-dialog"; + + @NotNull String SAVE_AS_DIALOG = "save-as-dialog"; + + @NotNull String GENERATE_LOD_DIALOG = "generate-lod-dialog"; + @NotNull String PLUGINS_DIALOG = "plugins-dialog"; + + @NotNull String CREATE_TERRAIN_DIALOG = "create-terrain-dialog"; + + @NotNull String NODE_SELECTOR_DIALOG = "node-selector-dialog"; + + @NotNull String ASSET_EDITOR_DIALOG = "asset-editor-dialog"; + @NotNull String ASSET_EDITOR_DIALOG_ACTIONS = "asset-editor-dialog-actions"; + @NotNull String ASSET_EDITOR_DIALOG_PREVIEW_CONTAINER = "asset-editor-dialog-preview-container"; + + @NotNull String OPEN_EXTERNAL_FOLDER_EDITOR_DIALOG = "open-external-folder-editor-dialog"; + + @NotNull String AUDIO_VIEW_EDITOR_CONTAINER = "audio-view-editor-container"; + + @NotNull String TEXT_EDITOR_TEXT_AREA = "text-editor-text-area"; + + @NotNull String IMAGE_VIEW_EDITOR_CONTAINER = "image-view-editor-container"; + + @NotNull String PHYSICS_NODE_LIST_CONTROL = "physics-node-list-control"; + + @NotNull String CUSTOM_TOOLTIP = "custom-tooltip"; + + @NotNull String IMAGE_CHANNEL_PREVIEW = "image-channel-preview"; + + @NotNull String IMAGE_PREVIEW = "image-preview"; + + @NotNull String PLUGIN_LIST_CELL = "plugin-list-cell"; + + @NotNull String STATS_3D_STATE = "stats-3dstate"; } \ No newline at end of file diff --git a/src/main/java/com/ss/editor/ui/css/CSSIds.java b/src/main/java/com/ss/editor/ui/css/CSSIds.java index 61b24d9c..9e3c93dc 100644 --- a/src/main/java/com/ss/editor/ui/css/CSSIds.java +++ b/src/main/java/com/ss/editor/ui/css/CSSIds.java @@ -1,5 +1,7 @@ package com.ss.editor.ui.css; +import org.jetbrains.annotations.NotNull; + /** * The list of css ids which used in the editor. * @@ -7,50 +9,20 @@ */ public interface CSSIds { - /** - * The constant ROOT. - */ - String ROOT = "Root"; - /** - * The constant ROOT_CONTAINER. - */ - String ROOT_CONTAINER = "RootContainer"; - - /** - * The constant EDITOR_MENU_BAR_COMPONENT. - */ - String EDITOR_MENU_BAR_COMPONENT = "EditorMenuBarComponent"; - - /** - * The constant EDITOR_AREA_COMPONENT. - */ - String EDITOR_AREA_COMPONENT = "EditorAreaComponent"; - - /** - * The constant GLOBAL_LEFT_TOOL_COMPONENT. - */ - String GLOBAL_LEFT_TOOL_COMPONENT = "GlobalLeftToolComponent"; - /** - * The constant GLOBAL_BOTTOM_TOOL_COMPONENT. - */ - String GLOBAL_BOTTOM_TOOL_COMPONENT = "GlobalBottomToolComponent"; - - /** - * The constant LOG_VIEW. - */ - String LOG_VIEW = "LogView"; - - /** - * The constant EDITOR_LOADING_LAYER. - */ - String EDITOR_LOADING_LAYER = "EditorLoadingLayer"; - /** - * The constant EDITOR_LOADING_PROGRESS. - */ - String EDITOR_LOADING_PROGRESS = "EditorLoadingProgress"; - - /** - * The constant ASSET_COMPONENT. - */ - String ASSET_COMPONENT = "AssetComponent"; + @NotNull String ROOT = "Root"; + @NotNull String ROOT_CONTAINER = "RootContainer"; + + @NotNull String EDITOR_MENU_BAR_COMPONENT = "EditorMenuBarComponent"; + + @NotNull String EDITOR_AREA_COMPONENT = "EditorAreaComponent"; + + @NotNull String GLOBAL_LEFT_TOOL_COMPONENT = "GlobalLeftToolComponent"; + @NotNull String GLOBAL_BOTTOM_TOOL_COMPONENT = "GlobalBottomToolComponent"; + + @NotNull String LOG_VIEW = "LogView"; + + @NotNull String EDITOR_LOADING_LAYER = "EditorLoadingLayer"; + @NotNull String EDITOR_LOADING_PROGRESS = "EditorLoadingProgress"; + + @NotNull String ASSET_COMPONENT = "AssetComponent"; } diff --git a/src/main/java/com/ss/editor/ui/css/CSSRegistry.java b/src/main/java/com/ss/editor/ui/css/CSSRegistry.java index 49e9d39d..4964c6b7 100644 --- a/src/main/java/com/ss/editor/ui/css/CSSRegistry.java +++ b/src/main/java/com/ss/editor/ui/css/CSSRegistry.java @@ -15,8 +15,8 @@ public class CSSRegistry { @NotNull private static final CSSRegistry INSTANCE = new CSSRegistry(); - @NotNull - public static CSSRegistry getInstance() { + @FromAnyThread + public static @NotNull CSSRegistry getInstance() { return INSTANCE; } @@ -45,8 +45,8 @@ public void register(@NotNull String cssFile) { * * @return the list of available css files. */ - @NotNull - public Array getAvailableCssFiles() { + @FromAnyThread + public @NotNull Array getAvailableCssFiles() { return availableCssFiles; } } diff --git a/src/main/java/com/ss/editor/ui/css/CssColorTheme.java b/src/main/java/com/ss/editor/ui/css/CssColorTheme.java index eb8da815..6667f1ba 100644 --- a/src/main/java/com/ss/editor/ui/css/CssColorTheme.java +++ b/src/main/java/com/ss/editor/ui/css/CssColorTheme.java @@ -1,5 +1,6 @@ package com.ss.editor.ui.css; +import com.ss.editor.annotation.FromAnyThread; import javafx.scene.paint.Color; import org.jetbrains.annotations.NotNull; @@ -48,30 +49,31 @@ public static CssColorTheme valueOf(final int index) { /** * @return the name of this theme. */ - @NotNull - public String getName() { + @FromAnyThread + public @NotNull String getName() { return name; } /** * @return the css file. */ - @NotNull - public String getCssFile() { + @FromAnyThread + public @NotNull String getCssFile() { return cssFile; } /** * @return the icon color. */ - @NotNull - public Color getIconColor() { + @FromAnyThread + public @NotNull Color getIconColor() { return iconColor; } /** * @return true if this theme is dark. */ + @FromAnyThread public boolean needRepaintIcons() { return true; } diff --git a/src/main/java/com/ss/editor/ui/scene/EditorFXScene.java b/src/main/java/com/ss/editor/ui/scene/EditorFXScene.java index 096525a8..a9216667 100644 --- a/src/main/java/com/ss/editor/ui/scene/EditorFXScene.java +++ b/src/main/java/com/ss/editor/ui/scene/EditorFXScene.java @@ -84,11 +84,6 @@ public class EditorFXScene extends Scene { @Nullable private Node focused; - /** - * Instantiates a new Editor fx scene. - * - * @param root the root - */ public EditorFXScene(@NotNull final Group root) { super(root); @@ -131,9 +126,8 @@ public EditorFXScene(@NotNull final Group root) { * * @return the view for drawing JME. */ - @NotNull @FXThread - public ImageView getCanvas() { + public @NotNull ImageView getCanvas() { return canvas; } @@ -156,9 +150,8 @@ public void hideCanvas() { * @param id the component id. * @return the component or null. */ - @Nullable @FXThread - public T findComponent(@NotNull final String id) { + public @Nullable T findComponent(@NotNull final String id) { final Array components = getComponents(); return unsafeCast(components.search(id, (component, toCheck) -> StringUtils.equals(toCheck, component.getComponentId()))); @@ -169,9 +162,8 @@ public T findComponent(@NotNull final String id) { * * @return the list of components. */ - @NotNull @FXThread - public Array getComponents() { + public @NotNull Array getComponents() { return components; } @@ -180,18 +172,16 @@ public Array getComponents() { * * @return the container of this scene. */ - @NotNull @FXThread - public StackPane getContainer() { + public @NotNull StackPane getContainer() { return container; } /** * @return the loading layer. */ - @NotNull @FXThread - private VBox getLoadingLayer() { + private @NotNull VBox getLoadingLayer() { return loadingLayer; } @@ -200,9 +190,8 @@ private VBox getLoadingLayer() { * * @return the hide layer. */ - @NotNull @FXThread - public StackPane getHideLayer() { + public @NotNull StackPane getHideLayer() { return hideLayer; } diff --git a/src/main/java/com/ss/editor/ui/tooltip/CustomTooltip.java b/src/main/java/com/ss/editor/ui/tooltip/CustomTooltip.java index ff6b92e3..c3068303 100644 --- a/src/main/java/com/ss/editor/ui/tooltip/CustomTooltip.java +++ b/src/main/java/com/ss/editor/ui/tooltip/CustomTooltip.java @@ -1,5 +1,6 @@ package com.ss.editor.ui.tooltip; +import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.css.CSSClasses; import com.ss.rlib.ui.util.FXUtils; import javafx.scene.Scene; @@ -24,7 +25,7 @@ public abstract class CustomTooltip extends Tooltip { /** * Instantiates a new Custom tooltip. */ - CustomTooltip() { + public CustomTooltip() { this.root = createRoot(); this.root.setStyle("-fx-background-color: -var-menu-background-color;"); @@ -37,28 +38,29 @@ public abstract class CustomTooltip extends Tooltip { } /** - * Gets root. + * Get the root container. * * @return the root container. */ - @NotNull - protected R getRoot() { + @FXThread + protected @NotNull R getRoot() { return root; } /** - * Create root r. + * Create the root container. * - * @return the r + * @return the root container. */ - @NotNull - protected abstract R createRoot(); + @FXThread + protected abstract @NotNull R createRoot(); /** * Create content of this tooltip. * - * @param root the root + * @param root the root container. */ + @FXThread protected void createContent(@NotNull final R root) { } } diff --git a/src/main/java/com/ss/editor/ui/tooltip/ImageChannelPreview.java b/src/main/java/com/ss/editor/ui/tooltip/ImageChannelPreview.java index 77bed962..f0125ac3 100644 --- a/src/main/java/com/ss/editor/ui/tooltip/ImageChannelPreview.java +++ b/src/main/java/com/ss/editor/ui/tooltip/ImageChannelPreview.java @@ -125,6 +125,7 @@ public class ImageChannelPreview extends CustomTooltip { } @Override + @FXThread protected @NotNull GridPane createRoot() { final GridPane gridPane = new GridPane(); FXUtils.addClassesTo(gridPane, CSSClasses.DEF_GRID_PANE, CSSClasses.IMAGE_CHANNEL_PREVIEW); @@ -200,6 +201,7 @@ public void clean() { /** * @param resourcePath the resource path. */ + @FXThread private void setResourcePath(@Nullable final String resourcePath) { this.resourcePath = resourcePath; } @@ -207,6 +209,7 @@ private void setResourcePath(@Nullable final String resourcePath) { /** * @return the resource path. */ + @FXThread private @Nullable String getResourcePath() { return resourcePath; } @@ -214,6 +217,7 @@ private void setResourcePath(@Nullable final String resourcePath) { /** * @param file the file. */ + @FXThread private void setFile(@Nullable final Path file) { this.file = file; } @@ -221,6 +225,7 @@ private void setFile(@Nullable final Path file) { /** * @return the file. */ + @FXThread private @Nullable Path getFile() { return file; } @@ -228,6 +233,7 @@ private void setFile(@Nullable final Path file) { /** * @param needToBuildFile true of need to build from the file. */ + @FXThread private void setNeedToBuildFile(final boolean needToBuildFile) { this.needToBuildFile = needToBuildFile; } @@ -235,6 +241,7 @@ private void setNeedToBuildFile(final boolean needToBuildFile) { /** * @return true of need to build from the file. */ + @FXThread private boolean isNeedToBuildFile() { return needToBuildFile; } @@ -242,6 +249,7 @@ private boolean isNeedToBuildFile() { /** * @param needToBuildResource true of need to build from the resource path. */ + @FXThread private void setNeedToBuildResource(final boolean needToBuildResource) { this.needToBuildResource = needToBuildResource; } @@ -249,11 +257,13 @@ private void setNeedToBuildResource(final boolean needToBuildResource) { /** * @return true of need to build from the resource path. */ + @FXThread private boolean isNeedToBuildResource() { return needToBuildResource; } @Override + @FXThread protected void show() { super.show(); @@ -286,6 +296,7 @@ protected void show() { } } + @FXThread private void buildPreview(@Nullable final Image image) { if (image == null || image.getWidth() != 120) { diff --git a/src/main/java/com/ss/editor/ui/tooltip/ImagePreview.java b/src/main/java/com/ss/editor/ui/tooltip/ImagePreview.java index 24c0f0b0..a2790288 100644 --- a/src/main/java/com/ss/editor/ui/tooltip/ImagePreview.java +++ b/src/main/java/com/ss/editor/ui/tooltip/ImagePreview.java @@ -40,6 +40,7 @@ public ImagePreview(@NotNull final Path path) { } @Override + @FXThread protected void createContent(@NotNull final BorderPane root) { super.createContent(root); @@ -51,11 +52,13 @@ protected void createContent(@NotNull final BorderPane root) { } @Override + @FXThread public void show(final Window owner) { super.show(owner); } @Override + @FXThread protected void show() { final ImageView imageView = getImageView(); @@ -67,6 +70,7 @@ protected void show() { } @Override + @FXThread protected @NotNull BorderPane createRoot() { final BorderPane pane = new BorderPane(); FXUtils.addClassesTo(pane, CSSClasses.IMAGE_PREVIEW); diff --git a/src/main/java/com/ss/editor/ui/util/DynamicIconSupport.java b/src/main/java/com/ss/editor/ui/util/DynamicIconSupport.java index 97ffd0e2..f5862805 100644 --- a/src/main/java/com/ss/editor/ui/util/DynamicIconSupport.java +++ b/src/main/java/com/ss/editor/ui/util/DynamicIconSupport.java @@ -1,5 +1,6 @@ package com.ss.editor.ui.util; +import com.ss.editor.annotation.FXThread; import com.ss.editor.config.EditorConfig; import com.ss.editor.manager.FileIconManager; import com.ss.editor.ui.css.CssColorTheme; @@ -52,6 +53,7 @@ public class DynamicIconSupport { * * @param buttons the buttons. */ + @FXThread public static void addSupport(@NotNull final ToggleButton... buttons) { for (final ToggleButton button : buttons) { addSupport(button); @@ -63,6 +65,7 @@ public static void addSupport(@NotNull final ToggleButton... buttons) { * * @param buttons the buttons. */ + @FXThread public static void addSupport(@NotNull final ButtonBase... buttons) { for (final ButtonBase button : buttons) { addSupport(button); @@ -74,6 +77,7 @@ public static void addSupport(@NotNull final ButtonBase... buttons) { * * @param button the button. */ + @FXThread public static void addSupport(@NotNull final ToggleButton button) { final EditorConfig editorConfig = EditorConfig.getInstance(); @@ -111,6 +115,7 @@ public static void addSupport(@NotNull final ToggleButton button) { * * @param button the button. */ + @FXThread public static void addSupport(@NotNull final ButtonBase button) { final EditorConfig editorConfig = EditorConfig.getInstance(); @@ -150,6 +155,7 @@ public static void addSupport(@NotNull final ButtonBase button) { * @param imageView the image view. * @param condition the condition of changing. */ + @FXThread public static void updateListener(@NotNull final Node node, @NotNull final ImageView imageView, @NotNull final ReadOnlyBooleanProperty condition) { updateListener(node, imageView, condition, SELECTED_IMAGE_LISTENER, NOT_SELECTED_IMAGE, SELECTED_IMAGE); @@ -162,11 +168,13 @@ public static void updateListener(@NotNull final Node node, @NotNull final Image * @param imageView the image view. * @param condition the condition of changing. */ + @FXThread public static void updateListener2(@NotNull final Node node, @NotNull final ImageView imageView, @NotNull final ReadOnlyBooleanProperty condition) { updateListener(node, imageView, condition, SELECTED_IMAGE_LISTENER_2, NOT_SELECTED_IMAGE_2, SELECTED_IMAGE_2); } + @FXThread private static void updateListener(@NotNull final Node node, @NotNull final ImageView imageView, @NotNull final ReadOnlyBooleanProperty condition, @NotNull final Object listenerKey, @NotNull final Object notSelectedKey, diff --git a/src/main/java/com/ss/editor/ui/util/UIUtils.java b/src/main/java/com/ss/editor/ui/util/UIUtils.java index cf37e2fe..4f75bc20 100644 --- a/src/main/java/com/ss/editor/ui/util/UIUtils.java +++ b/src/main/java/com/ss/editor/ui/util/UIUtils.java @@ -64,6 +64,7 @@ public class UIUtils { * @param pane the pane. * @param controls the controls. */ + @FXThread public static void addFocusBinding(@NotNull final Pane pane, @NotNull final Control... controls) { final BooleanProperty focused = new BooleanPropertyBase(false) { @@ -670,6 +671,7 @@ public static void openSaveAsDialog(@NotNull final Consumer<@NotNull Path> handl * @param dragEvent the drag event. * @param extensions the extensions. */ + @FXThread public static void acceptIfHasFile(@NotNull final DragEvent dragEvent, @NotNull final Array extensions) { final Dragboard dragboard = dragEvent.getDragboard(); @@ -688,6 +690,7 @@ public static void acceptIfHasFile(@NotNull final DragEvent dragEvent, @NotNull * @param dragEvent the drag event. * @param targetExtension the extension. */ + @FXThread public static void acceptIfHasFile(@NotNull final DragEvent dragEvent, @NotNull final String targetExtension) { final Dragboard dragboard = dragEvent.getDragboard(); @@ -708,6 +711,7 @@ public static void acceptIfHasFile(@NotNull final DragEvent dragEvent, @NotNull * @param extensions the extensions. * @return true if there are required file. */ + @FXThread public static boolean isHasFile(@NotNull final Dragboard dragboard, @NotNull final Array extensions) { final List files = unsafeCast(dragboard.getContent(DataFormat.FILES)); @@ -729,6 +733,7 @@ public static boolean isHasFile(@NotNull final Dragboard dragboard, @NotNull fin * @param targetExtension the target extension. * @return true if there are required file. */ + @FXThread public static boolean isHasFile(@NotNull final Dragboard dragboard, @NotNull final String targetExtension) { final List files = unsafeCast(dragboard.getContent(DataFormat.FILES)); @@ -750,6 +755,7 @@ public static boolean isHasFile(@NotNull final Dragboard dragboard, @NotNull fin * @param extensions the extensions. * @param handler the handler. */ + @FXThread public static void handleDroppedFile(@NotNull final DragEvent dragEvent, @NotNull final Array extensions, @NotNull final Consumer handler) { @@ -779,6 +785,7 @@ public static void handleDroppedFile(@NotNull final DragEvent dragEvent, @NotNul * @param firstArg the first argument. * @param handler the handler. */ + @FXThread public static void handleDroppedFile(@NotNull final DragEvent dragEvent, @NotNull final Array extensions, @NotNull final F firstArg, @NotNull final BiConsumer handler) { @@ -811,6 +818,7 @@ public static void handleDroppedFile(@NotNull final DragEvent dragEvent, * @param secondArg the second argument. * @param handler the handler. */ + @FXThread public static void handleDroppedFile(@NotNull final DragEvent dragEvent, @NotNull final String targetExtension, @NotNull final F firstArg, @NotNull final S secondArg, @@ -830,6 +838,7 @@ public static void handleDroppedFile(@NotNull final DragEvent dragEvent, * @param secondArg the second argument. * @param handler the handler. */ + @FXThread public static void handleDroppedFile(@NotNull final Dragboard dragboard, @NotNull final String targetExtension, @NotNull final F firstArg, @NotNull final S secondArg, @@ -862,6 +871,7 @@ public static void handleDroppedFile(@NotNull final Dragboard dragboard, * @param secondArg the second argument. * @param handler the handler. */ + @FXThread public static void handleDroppedFile(@NotNull final DragEvent dragEvent, @NotNull final Array extensions, @NotNull final F firstArg, @NotNull final S secondArg, From 83bb09737a5af448fdadbb11f5cb8c0fa40bf597 Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Mon, 18 Sep 2017 07:01:17 +0300 Subject: [PATCH 21/50] fixed asset dialogs, updated API. --- .../material/BaseMaterialEditor3DState.java | 12 ++++- .../tree/resource/VirtualResourceElement.java | 5 ++ .../VirtualResourceElementFactory.java | 42 +++++++++++---- .../code/MaterialDefinitionCodeArea.java | 6 +-- .../asset/file/FileAssetEditorDialog.java | 24 +++------ .../asset/file/FolderAssetEditorDialog.java | 21 -------- .../StringVirtualAssetEditorDialog.java | 8 +-- .../java/com/ss/editor/ui/util/UIUtils.java | 1 - .../java/com/ss/editor/util/EditorUtil.java | 53 ++++++++++++------- 9 files changed, 99 insertions(+), 73 deletions(-) diff --git a/src/main/java/com/ss/editor/plugin/api/editor/material/BaseMaterialEditor3DState.java b/src/main/java/com/ss/editor/plugin/api/editor/material/BaseMaterialEditor3DState.java index 2bde8c40..551a1b13 100644 --- a/src/main/java/com/ss/editor/plugin/api/editor/material/BaseMaterialEditor3DState.java +++ b/src/main/java/com/ss/editor/plugin/api/editor/material/BaseMaterialEditor3DState.java @@ -194,13 +194,23 @@ protected void updateMaterialImpl(@NotNull final Material material) { try { renderManager.preloadScene(testBox); } catch (final RendererException | AssetNotFoundException | UnsupportedOperationException e) { - EditorUtil.handleException(LOGGER, this, e); + handleMaterialException(e); testBox.setMaterial(EDITOR.getDefaultMaterial()); testQuad.setMaterial(EDITOR.getDefaultMaterial()); testSphere.setMaterial(EDITOR.getDefaultMaterial()); } } + /** + * Handle the material exception. + * + * @param exception the exception. + */ + @JMEThread + protected void handleMaterialException(@NotNull final RuntimeException exception) { + EditorUtil.handleException(LOGGER, this, exception); + } + /** * Change the {@link ModelType}. * diff --git a/src/main/java/com/ss/editor/ui/component/virtual/tree/resource/VirtualResourceElement.java b/src/main/java/com/ss/editor/ui/component/virtual/tree/resource/VirtualResourceElement.java index 9ed853ae..f3d1a2fd 100644 --- a/src/main/java/com/ss/editor/ui/component/virtual/tree/resource/VirtualResourceElement.java +++ b/src/main/java/com/ss/editor/ui/component/virtual/tree/resource/VirtualResourceElement.java @@ -113,4 +113,9 @@ public boolean hasChildren() { public int compareTo(@NotNull final VirtualResourceElement o) { return 0; } + + @Override + public String toString() { + return object.toString(); + } } diff --git a/src/main/java/com/ss/editor/ui/component/virtual/tree/resource/VirtualResourceElementFactory.java b/src/main/java/com/ss/editor/ui/component/virtual/tree/resource/VirtualResourceElementFactory.java index 2fc7f5c9..a023af26 100644 --- a/src/main/java/com/ss/editor/ui/component/virtual/tree/resource/VirtualResourceElementFactory.java +++ b/src/main/java/com/ss/editor/ui/component/virtual/tree/resource/VirtualResourceElementFactory.java @@ -46,13 +46,23 @@ public class VirtualResourceElementFactory { path = parent; parent = FileUtils.getParent(path, '/'); } + + final Array children = parentToChildren.get("/", () -> ArrayFactory.newArray(String.class)); + if (!children.contains(path)) { + children.add(path); + } }); final ObjectDictionary> pathToResult = DictionaryFactory.newObjectDictionary(); parentToChildren.forEach((path, children) -> { - if (!pathToResource.containsKey(path)) { + + final T resource = pathToResource.get(path); + + if (resource == null) { pathToResult.put(path, new FolderVirtualResourceElement(resourceTree, path)); + } else { + pathToResult.put(path, new ObjectVirtualResourceElement<>(resourceTree, resource)); } }); @@ -62,23 +72,37 @@ public class VirtualResourceElementFactory { if (parent == null) return; for (final String child : children) { + final T resource = pathToResource.get(child); - final VirtualResourceElement element = pathToResult.get(child); - if (resource != null) { - parent.addChild(new ObjectVirtualResourceElement<>(resourceTree, resource)); - } else if (element != null) { + VirtualResourceElement element = pathToResult.get(child); + + if (element == null && resource != null) { + element = new ObjectVirtualResourceElement<>(resourceTree, resource); + pathToResult.put(child, element); + } + + if (element != null) { parent.addChild(element); } } }); final RootVirtualResourceElement root = new RootVirtualResourceElement(resourceTree); + final Array rootPaths = parentToChildren.get("/"); + + if (rootPaths == null) { + return root; + } - pathToResult.forEach((path, element) -> { - final String parent = FileUtils.getParent(path, '/'); - if(StringUtils.equals(parent, path)) { - root.addChild(element); + rootPaths.forEach(path -> { + + final VirtualResourceElement element = pathToResult.get(path); + + if (element == null) { + return; } + + root.addChild(element); }); return root; diff --git a/src/main/java/com/ss/editor/ui/control/code/MaterialDefinitionCodeArea.java b/src/main/java/com/ss/editor/ui/control/code/MaterialDefinitionCodeArea.java index 4f7e20a8..27053b55 100644 --- a/src/main/java/com/ss/editor/ui/control/code/MaterialDefinitionCodeArea.java +++ b/src/main/java/com/ss/editor/ui/control/code/MaterialDefinitionCodeArea.java @@ -42,9 +42,9 @@ public class MaterialDefinitionCodeArea extends BaseCodeArea { "GLSL100", "GLSL110", "GLSL120", "GLSL130", "GLSL140", "GLSL150", "GLSL400", "GLSL330", "GLSL410", "GLSL420", "GLSL430", "GLSL440", "GLSL450", // attributes - "Tangent", "Binormal", "InterleavedData", "Index", "BindPosePosition", "BindPoseNormal", - "BoneWeight", "BoneIndex", "BindPoseTangent", "HWBoneWeight", "HWBoneIndex", "InstanceData", - "Position", "Size", "Normal", "TexCoord", "Color", + "inTangent", "inBinormal", "inInterleavedData", "inIndex", "inBindPosePosition", "inBindPoseNormal", + "inBoneWeight", "inBoneIndex", "inBindPoseTangent", "inHWBoneWeight", "inHWBoneIndex", "inInstanceData", + "inPosition", "inSize", "Normal", "inTexCoord", "inColor", // uniforms "WorldViewProjectionMatrix", "Time", "NormalMatrix", "WorldViewMatrix", "ViewMatrix", "CameraPosition", "WorldMatrix", "Resolution", "ViewProjectionMatrix", "ProjectionMatrix", "NormalMatrix", "WorldMatrixInverseTranspose", diff --git a/src/main/java/com/ss/editor/ui/dialog/asset/file/FileAssetEditorDialog.java b/src/main/java/com/ss/editor/ui/dialog/asset/file/FileAssetEditorDialog.java index 74376111..dd64ff9f 100644 --- a/src/main/java/com/ss/editor/ui/dialog/asset/file/FileAssetEditorDialog.java +++ b/src/main/java/com/ss/editor/ui/dialog/asset/file/FileAssetEditorDialog.java @@ -46,22 +46,14 @@ protected void processOpen(@NotNull final ResourceElement element) { protected void validate(@NotNull final Label warningLabel, @Nullable final ResourceElement element) { super.validate(warningLabel, element); - if (element == null) { - LOGGER.warning(this, "The element is null."); - return; + final Function<@NotNull Path, @Nullable String> validator = getValidator(); + final boolean visible = warningLabel.isVisible(); + + if (!visible && element instanceof FolderResourceElement) { + warningLabel.setText(Messages.ASSET_EDITOR_DIALOG_WARNING_SELECT_FILE); + warningLabel.setVisible(true); + } else if (validator == null) { + warningLabel.setVisible(false); } - - final Function validator = getValidator(); - String message = validator == null ? null : validator.apply(element.getFile()); - - if (message == null && element instanceof FolderResourceElement ) { - message = Messages.ASSET_EDITOR_DIALOG_WARNING_SELECT_FILE; - } - - if (message != null) { - warningLabel.setText(message); - } - - warningLabel.setVisible(message != null); } } diff --git a/src/main/java/com/ss/editor/ui/dialog/asset/file/FolderAssetEditorDialog.java b/src/main/java/com/ss/editor/ui/dialog/asset/file/FolderAssetEditorDialog.java index 3caf8c69..0220cf3a 100644 --- a/src/main/java/com/ss/editor/ui/dialog/asset/file/FolderAssetEditorDialog.java +++ b/src/main/java/com/ss/editor/ui/dialog/asset/file/FolderAssetEditorDialog.java @@ -2,7 +2,6 @@ import com.ss.editor.annotation.FXThread; import com.ss.editor.ui.component.asset.tree.resource.ResourceElement; -import javafx.scene.control.Label; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -41,24 +40,4 @@ protected void processOpen(@NotNull final ResourceElement element) { protected @Nullable Path getObject(@NotNull final ResourceElement element) { return element.getFile(); } - - @Override - @FXThread - protected void validate(@NotNull final Label warningLabel, @Nullable final ResourceElement element) { - super.validate(warningLabel, element); - - if (element == null) { - LOGGER.warning(this, "The element is null."); - return; - } - - final Function validator = getValidator(); - String message = validator == null ? null : validator.apply(element.getFile()); - - if (message != null) { - warningLabel.setText(message); - } - - warningLabel.setVisible(message != null); - } } diff --git a/src/main/java/com/ss/editor/ui/dialog/asset/virtual/StringVirtualAssetEditorDialog.java b/src/main/java/com/ss/editor/ui/dialog/asset/virtual/StringVirtualAssetEditorDialog.java index 4b24fe7f..3a612153 100644 --- a/src/main/java/com/ss/editor/ui/dialog/asset/virtual/StringVirtualAssetEditorDialog.java +++ b/src/main/java/com/ss/editor/ui/dialog/asset/virtual/StringVirtualAssetEditorDialog.java @@ -1,5 +1,6 @@ package com.ss.editor.ui.dialog.asset.virtual; +import com.ss.editor.Messages; import com.ss.editor.annotation.FromAnyThread; import com.ss.rlib.util.FileUtils; import com.ss.rlib.util.StringUtils; @@ -17,12 +18,13 @@ */ public class StringVirtualAssetEditorDialog extends VirtualAssetEditorDialog { - private static final Function<@NotNull String, @Nullable String> DEFAULT_VALIDATOR = s -> { + @NotNull + public static final Function<@NotNull String, @Nullable String> DEFAULT_VALIDATOR = resource -> { - final String extension = FileUtils.getExtension(s); + final String extension = FileUtils.getExtension(resource); if (StringUtils.isEmpty(extension)) { - return "You need to select a file"; + return Messages.ASSET_EDITOR_DIALOG_WARNING_SELECT_FILE; } return null; diff --git a/src/main/java/com/ss/editor/ui/util/UIUtils.java b/src/main/java/com/ss/editor/ui/util/UIUtils.java index 4f75bc20..cffaca5f 100644 --- a/src/main/java/com/ss/editor/ui/util/UIUtils.java +++ b/src/main/java/com/ss/editor/ui/util/UIUtils.java @@ -606,7 +606,6 @@ public static void openResourceAssetDialog(@NotNull final Consumer handl public static void openResourceAssetDialog(@NotNull final Consumer handler, @Nullable final Function validator, @NotNull final Array resources) { - final StringVirtualAssetEditorDialog dialog = new StringVirtualAssetEditorDialog(handler, validator, resources); dialog.show(); } diff --git a/src/main/java/com/ss/editor/util/EditorUtil.java b/src/main/java/com/ss/editor/util/EditorUtil.java index 16783b53..4511641d 100644 --- a/src/main/java/com/ss/editor/util/EditorUtil.java +++ b/src/main/java/com/ss/editor/util/EditorUtil.java @@ -364,7 +364,9 @@ public static void handleException(@Nullable final Logger logger, @Nullable fina */ public static void handleException(@Nullable Logger logger, @Nullable final Object owner, @NotNull final Exception e, @Nullable final Runnable callback) { - if (logger == null) logger = LOGGER; + if (logger == null) { + logger = LOGGER; + } if (owner == null) { logger.warning(e); @@ -377,34 +379,47 @@ public static void handleException(@Nullable Logger logger, @Nullable final Obje GAnalytics.sendException(e, false); - StringWriter writer = new StringWriter(); - PrintWriter printWriter = new PrintWriter(writer); + final String localizedMessage = e.getLocalizedMessage(); + final String stackTrace = buildStackTrace(e); + + final Alert alert = createErrorAlert(e, localizedMessage, stackTrace); + alert.show(); + alert.setWidth(500); + alert.setHeight(220); - e.printStackTrace(printWriter); + if (callback != null) alert.setOnHidden(event -> callback.run()); + }); + } - final String localizedMessage = e.getLocalizedMessage(); + /** + * Build the stack trace of the exception. + * + * @param exception the exception. + * @return the built stack trace. + */ + @FromAnyThread + public static String buildStackTrace(@NotNull final Exception exception) { - String stackTrace = writer.toString(); + StringWriter writer = new StringWriter(); + PrintWriter printWriter = new PrintWriter(writer); - int level = 0; + exception.printStackTrace(printWriter); - for (Throwable cause = e.getCause(); cause != null && level < 6; cause = cause.getCause(), level++) { + String stackTrace = writer.toString(); - writer = new StringWriter(); - printWriter = new PrintWriter(writer); + int level = 0; - cause.printStackTrace(printWriter); + for (Throwable cause = exception.getCause(); cause != null && level < 6; cause = cause.getCause(), level++) { - stackTrace += "\n caused by " + writer.toString(); - } + writer = new StringWriter(); + printWriter = new PrintWriter(writer); - final Alert alert = createErrorAlert(e, localizedMessage, stackTrace); - alert.show(); - alert.setWidth(500); - alert.setHeight(220); + cause.printStackTrace(printWriter); - if (callback != null) alert.setOnHidden(event -> callback.run()); - }); + stackTrace += "\n caused by " + writer.toString(); + } + + return stackTrace; } /** From 0766986cbfae2a6d9f9546767e0a4c5b73d45c11 Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Mon, 18 Sep 2017 07:10:49 +0300 Subject: [PATCH 22/50] fixed max distance of a camera, updated icon color for light theme. --- src/main/java/com/ss/editor/Editor.java | 2 +- src/main/java/com/ss/editor/ui/css/CssColorTheme.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/ss/editor/Editor.java b/src/main/java/com/ss/editor/Editor.java index 5c682e51..e4ba6693 100644 --- a/src/main/java/com/ss/editor/Editor.java +++ b/src/main/java/com/ss/editor/Editor.java @@ -281,7 +281,7 @@ public void simpleInitApp() { audioRenderer.setEnvironment(new Environment(Environment.Garage)); viewPort.setBackgroundColor(new ColorRGBA(50 / 255F, 50 / 255F, 50 / 255F, 1F)); - cam.setFrustumPerspective(55, (float) cam.getWidth() / cam.getHeight(), 1f, 10000); + cam.setFrustumPerspective(55, (float) cam.getWidth() / cam.getHeight(), 1f, Integer.MAX_VALUE); defaultMaterial = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); diff --git a/src/main/java/com/ss/editor/ui/css/CssColorTheme.java b/src/main/java/com/ss/editor/ui/css/CssColorTheme.java index 6667f1ba..e3eb6304 100644 --- a/src/main/java/com/ss/editor/ui/css/CssColorTheme.java +++ b/src/main/java/com/ss/editor/ui/css/CssColorTheme.java @@ -10,7 +10,7 @@ * @author JavaSaBr */ public enum CssColorTheme { - LIGHT("/ui/css/light-color.css", "White", Color.BLACK), + LIGHT("/ui/css/light-color.css", "White", Color.web("#5d626e")), SHADOW("/ui/css/shadow-color.css", "Shadow", Color.web("#c8d2e1")), DARK("/ui/css/dark-color.css", "Dark", Color.web("#c8dae2")),; From a9c3b3fd3ca6c7816d1387b2d3bfb3d603426724 Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Mon, 18 Sep 2017 07:14:11 +0300 Subject: [PATCH 23/50] added temp overwriting some shader classes. --- .../materialdef/J3mdTechniqueDefWriter.java | 299 ++++++++ .../jme3/shader/Glsl100ShaderGenerator.java | 642 ++++++++++++++++++ .../jme3/shader/Glsl150ShaderGenerator.java | 165 +++++ .../java/com/jme3/shader/ShaderGenerator.java | 369 ++++++++++ 4 files changed, 1475 insertions(+) create mode 100644 src/main/java/com/jme3/material/plugin/export/materialdef/J3mdTechniqueDefWriter.java create mode 100644 src/main/java/com/jme3/shader/Glsl100ShaderGenerator.java create mode 100644 src/main/java/com/jme3/shader/Glsl150ShaderGenerator.java create mode 100644 src/main/java/com/jme3/shader/ShaderGenerator.java diff --git a/src/main/java/com/jme3/material/plugin/export/materialdef/J3mdTechniqueDefWriter.java b/src/main/java/com/jme3/material/plugin/export/materialdef/J3mdTechniqueDefWriter.java new file mode 100644 index 00000000..b20a220e --- /dev/null +++ b/src/main/java/com/jme3/material/plugin/export/materialdef/J3mdTechniqueDefWriter.java @@ -0,0 +1,299 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.jme3.material.plugin.export.materialdef; + +import com.jme3.material.MatParam; +import com.jme3.material.RenderState; +import com.jme3.material.TechniqueDef; +import com.jme3.shader.Shader; +import com.jme3.shader.ShaderNode; +import com.jme3.shader.UniformBinding; +import com.jme3.shader.VariableMapping; + +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.util.Collection; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author nehon + */ +public class J3mdTechniqueDefWriter { + + public J3mdTechniqueDefWriter() { + } + + public void write(TechniqueDef techniqueDef, Collection matParams, OutputStreamWriter out) throws IOException { + out.write(" Technique"); + if(!techniqueDef.getName().equals("Default")) { + out.write(" "); + out.write(techniqueDef.getName()); + } + out.write(" {\n"); + + //Light mode + if(techniqueDef.getLightMode() != TechniqueDef.LightMode.Disable ){ + out.write(" LightMode "); + out.write(techniqueDef.getLightMode().name()); + out.write("\n\n"); + } + + //Shadow mode + if(techniqueDef.getShadowMode() != TechniqueDef.ShadowMode.Disable ){ + out.write(" ShadowMode "); + out.write(techniqueDef.getShadowMode().name()); + out.write("\n\n"); + } + + //Shaders + if(!techniqueDef.isUsingShaderNodes()) { + writeShaders(techniqueDef, out); + } + + //World params + if(!techniqueDef.getWorldBindings().isEmpty()){ + writeWorldParams(techniqueDef, out); + } + + //ShaderNodes + if(techniqueDef.isUsingShaderNodes()){ + writeShaderNodes(techniqueDef, matParams, out); + + } else { + //When we have ShaderNodes, Defines are handled differently so we don't have to write them. + //Defines + if (techniqueDef.getDefineNames().length != 0) { + writeDefines(techniqueDef, matParams, out); + } + } + + //render state + RenderState rs = techniqueDef.getRenderState(); + if(rs != null){ + out.write(" RenderState {\n"); + writeRenderState(rs, out); + out.write(" }\n\n"); + } + + //forced render state + rs = techniqueDef.getForcedRenderState(); + if(rs != null){ + out.write(" ForcedRenderState {\n"); + writeRenderState(rs, out); + out.write(" }\n\n"); + } + + //no render + if(techniqueDef.isNoRender()){ + out.write(" NoRender\n\n"); + } + + out.write(" }\n"); + } + + private void writeDefines(TechniqueDef techniqueDef, Collection matParams, OutputStreamWriter out) throws IOException { + out.write(" Defines {\n"); + + for (int i = 0; i < techniqueDef.getDefineNames().length; i++) { + String matParamName = getMatParamNameForDefineId(techniqueDef, matParams, i); + if (matParamName != null) { + String defineName = techniqueDef.getDefineNames()[i]; + out.write(" "); + out.write(defineName); + out.write(": "); + out.write(matParamName); + out.write("\n"); + } + } + out.write(" }\n\n"); + } + + private void writeShaderNodes(TechniqueDef techniqueDef, Collection matParams, OutputStreamWriter out) throws IOException { + out.write(" VertexShaderNodes {\n"); + for (ShaderNode shaderNode : techniqueDef.getShaderNodes()) { + if(shaderNode.getDefinition().getType() == Shader.ShaderType.Vertex){ + writeShaderNode(out, shaderNode, matParams); + } + } + out.write(" }\n\n"); + + out.write(" FragmentShaderNodes {\n"); + for (ShaderNode shaderNode : techniqueDef.getShaderNodes()) { + if(shaderNode.getDefinition().getType() == Shader.ShaderType.Fragment){ + writeShaderNode(out, shaderNode, matParams); + } + } + out.write(" }\n\n"); + } + + private void writeWorldParams(TechniqueDef techniqueDef, OutputStreamWriter out) throws IOException { + out.write(" WorldParameters {\n"); + for (UniformBinding uniformBinding : techniqueDef.getWorldBindings()) { + out.write(" "); + out.write(uniformBinding.toString()); + out.write("\n"); + } + out.write(" }\n\n"); + } + + private void writeShaders(TechniqueDef techniqueDef, OutputStreamWriter out) throws IOException { + if (techniqueDef.getShaderProgramNames().size() > 0) { + for (Shader.ShaderType shaderType : techniqueDef.getShaderProgramNames().keySet()) { + // System.err.println(shaderType + " " +techniqueDef.getShaderProgramNames().get(shaderType) + " " +techniqueDef.getShaderProgramLanguage(shaderType)) + out.write(" "); + out.write(shaderType.name()); + out.write("Shader "); + out.write(techniqueDef.getShaderProgramLanguage(shaderType)); + out.write(": "); + out.write(techniqueDef.getShaderProgramNames().get(shaderType)); + out.write("\n"); + } + out.write("\n"); + } + } + + private void writeShaderNode( OutputStreamWriter out, ShaderNode shaderNode, Collection matParams) throws IOException { + out.write(" ShaderNode "); + out.write(shaderNode.getName()); + out.write(" {\n"); + + if (shaderNode.getCondition() != null){ + out.write(" Condition: "); + out.write(formatCondition(shaderNode.getCondition(), matParams)); + out.write("\n"); + } + + out.write(" Definition: "); + out.write(shaderNode.getDefinition().getName()); + out.write(": "); + out.write(shaderNode.getDefinition().getPath()); + out.write("\n"); + + out.write(" InputMappings {\n"); + for (VariableMapping mapping : shaderNode.getInputMapping()) { + writeVariableMapping(out, shaderNode, mapping, matParams); + } + out.write(" }\n"); + + out.write(" OutputMappings {\n"); + for (VariableMapping mapping : shaderNode.getOutputMapping()) { + writeVariableMapping(out, shaderNode, mapping, matParams); + } + out.write(" }\n"); + + + out.write(" }\n"); + } + + private void writeVariableMapping(OutputStreamWriter out, ShaderNode shaderNode, VariableMapping mapping, Collection matParams) throws IOException { + out.write(" "); + if(!mapping.getLeftVariable().getNameSpace().equals(shaderNode.getName())) { + out.write(mapping.getLeftVariable().getNameSpace()); + out.write("."); + } + out.write(mapping.getLeftVariable().getName()); + if(!mapping.getLeftSwizzling().equals("")){ + out.write("."); + out.write(mapping.getLeftSwizzling()); + } + out.write(" = "); + if(!mapping.getRightVariable().getNameSpace().equals(shaderNode.getName())) { + out.write(mapping.getRightVariable().getNameSpace()); + out.write("."); + } + out.write(mapping.getRightVariable().getName().replaceFirst("g_","").replaceFirst("m_","")); + if(!mapping.getRightSwizzling().equals("")){ + out.write("."); + out.write(mapping.getRightSwizzling()); + } + + if (mapping.getCondition() != null){ + out.write(" : "); + out.write(formatCondition(mapping.getCondition(),matParams)); + } + + out.write("\n"); + } + + private String formatCondition(String condition, Collection matParams){ + //condition = condition.replaceAll("defined\\(",""); + + String res = condition; + Pattern pattern = Pattern.compile("defined\\(([A-Z0-9]*)\\)"); + Matcher m = pattern.matcher(condition); + + while(m.find()){ + String match = m.group(0); + String defineName = m.group(1).toLowerCase(); + for (MatParam matParam : matParams) { + if(matParam.getName().toLowerCase().equals(defineName)){ + res = res.replace(match, matParam.getName()); + } + } + } + + return res; + } + + private void writeRenderStateAttribute(OutputStreamWriter out, String name, String value) throws IOException { + out.write(" "); + out.write(name); + out.write(" "); + out.write(value); + out.write("\n"); + } + + private void writeRenderState(RenderState rs, OutputStreamWriter out) throws IOException { + RenderState defRs = RenderState.DEFAULT; + if(rs.getBlendMode() != defRs.getBlendMode()) { + writeRenderStateAttribute(out, "Blend", rs.getBlendMode().name()); + } + if(rs.isWireframe() != defRs.isWireframe()) { + writeRenderStateAttribute(out, "Wireframe", rs.isWireframe()?"On":"Off"); + } + if(rs.getFaceCullMode() != defRs.getFaceCullMode()) { + writeRenderStateAttribute(out, "FaceCull", rs.getFaceCullMode().name()); + } + if(rs.isDepthWrite() != defRs.isDepthWrite()) { + writeRenderStateAttribute(out, "DepthWrite", rs.isDepthWrite()?"On":"Off"); + } + if(rs.isDepthTest() != defRs.isDepthTest()) { + writeRenderStateAttribute(out, "DepthTest", rs.isDepthTest()?"On":"Off"); + } + if(rs.getBlendEquation() != defRs.getBlendEquation()) { + writeRenderStateAttribute(out, "BlendEquation", rs.getBlendEquation().name()); + } + if(rs.getBlendEquationAlpha() != defRs.getBlendEquationAlpha()) { + writeRenderStateAttribute(out, "BlendEquationAlpha", rs.getBlendEquationAlpha().name()); + } + if(rs.getPolyOffsetFactor() != defRs.getPolyOffsetFactor() || rs.getPolyOffsetUnits() != defRs.getPolyOffsetUnits()) { + writeRenderStateAttribute(out, "PolyOffset", rs.getPolyOffsetFactor() + " " + rs.getPolyOffsetUnits()); + } + if(rs.isColorWrite() != defRs.isColorWrite()) { + writeRenderStateAttribute(out, "ColorWrite", rs.isColorWrite()?"On":"Off"); + } + if(rs.getDepthFunc() != defRs.getDepthFunc()) { + writeRenderStateAttribute(out, "DepthFunc", rs.getDepthFunc().name()); + } + if(rs.getLineWidth() != defRs.getLineWidth()) { + writeRenderStateAttribute(out, "LineWidth", Float.toString(rs.getLineWidth())); + } + } + + private String getMatParamNameForDefineId(TechniqueDef techniqueDef, Collection matParams, int defineId) { + for (MatParam matParam : matParams) { + Integer id = techniqueDef.getShaderParamDefineId(matParam.getName()); + if(id !=null && id == defineId){ + return matParam.getName(); + } + } + return null; + } + +} + + diff --git a/src/main/java/com/jme3/shader/Glsl100ShaderGenerator.java b/src/main/java/com/jme3/shader/Glsl100ShaderGenerator.java new file mode 100644 index 00000000..6510c352 --- /dev/null +++ b/src/main/java/com/jme3/shader/Glsl100ShaderGenerator.java @@ -0,0 +1,642 @@ +/* + * Copyright (c) 2009-2012 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.shader; + +import com.jme3.asset.AssetManager; +import com.jme3.material.ShaderGenerationInfo; +import com.jme3.material.plugins.ConditionParser; +import com.jme3.shader.Shader.ShaderType; + +import java.util.ArrayList; +import java.util.List; + +/** + * This shader Generator can generate Vertex and Fragment shaders from + * shadernodes for GLSL 1.0 + * + * @author Nehon + */ +public class Glsl100ShaderGenerator extends ShaderGenerator { + + private ShaderNodeVariable inPosTmp; + + /** + * creates a Glsl100ShaderGenerator + * @param assetManager the assetManager + */ + public Glsl100ShaderGenerator(AssetManager assetManager) { + super(assetManager); + } + + @Override + protected void generateUniforms(StringBuilder source, ShaderGenerationInfo info, ShaderType type) { + generateUniforms(source, type == ShaderType.Vertex ? info.getVertexUniforms() : info.getFragmentUniforms()); + } + + /** + * declare a list of uniforms + * + * @param source the source to append to + * @param uniforms the list of uniforms + */ + protected void generateUniforms(StringBuilder source, List uniforms) { + source.append("\n"); + for (ShaderNodeVariable var : uniforms) { + declareVariable(source, var, false, "uniform"); + } + } + + /** + * {@inheritDoc} + * + * attributes are all declared, inPositon is declared even if it's not in + * the list and it's condition is nulled. + */ + @Override + protected void generateAttributes(StringBuilder source, ShaderGenerationInfo info) { + source.append("\n"); + boolean inPosition = false; + for (ShaderNodeVariable var : info.getAttributes()) { + if (var.getName().equals("inPosition")) { + inPosition = true; + var.setCondition(null); + fixInPositionType(var); + //keep track on the InPosition variable to avoid iterating through attributes again + inPosTmp = var; + } + declareAttribute(source, var); + + } + if (!inPosition) { + inPosTmp = new ShaderNodeVariable("vec3", "inPosition"); + declareAttribute(source, inPosTmp); + } + + } + + @Override + protected void generateVaryings(StringBuilder source, ShaderGenerationInfo info, ShaderType type) { + source.append("\n"); + for (ShaderNodeVariable var : info.getVaryings()) { + declareVarying(source, var, type == ShaderType.Vertex ? false : true); + } + } + + /** + * {@inheritDoc} + * + * if the declaration contains no code nothing is done, else it's appended + */ + @Override + protected void generateDeclarativeSection(StringBuilder source, ShaderNode shaderNode, String nodeSource, ShaderGenerationInfo info) { + if (nodeSource.replaceAll("\\n", "").trim().length() > 0) { + nodeSource = updateDefinesName(nodeSource, shaderNode); + source.append("\n"); + unIndent(); + startCondition(shaderNode.getCondition(), source); + source.append(nodeSource); + source.append("\n"); + endCondition(shaderNode.getCondition(), source); + indent(); + } + } + + /** + * {@inheritDoc} + * + * Shader outputs are declared and initialized inside the main section + */ + @Override + protected void generateStartOfMainSection(StringBuilder source, ShaderGenerationInfo info, ShaderType type) { + source.append("\n"); + source.append("void main() {\n"); + indent(); + appendIndent(source); + if (type == ShaderType.Vertex) { + declareGlobalPosition(info, source); + } else if (type == ShaderType.Fragment) { + for (ShaderNodeVariable global : info.getFragmentGlobals()) { + declareVariable(source, global, "vec4(1.0)"); + } + } + source.append("\n"); + } + + /** + * {@inheritDoc} + * + * outputs are assigned to built in glsl output. then the main section is + * closed + * + * This code accounts for multi render target and correctly output to + * gl_FragData if several output are declared for the fragment shader + */ + @Override + protected void generateEndOfMainSection(StringBuilder source, ShaderGenerationInfo info, ShaderType type) { + source.append("\n"); + if (type == ShaderType.Vertex) { + appendOutput(source, "gl_Position", info.getVertexGlobal()); + } else if (type == ShaderType.Fragment) { + List globals = info.getFragmentGlobals(); + if (globals.size() == 1) { + appendOutput(source, "gl_FragColor", globals.get(0)); + } else { + int i = 0; + //Multi Render Target + for (ShaderNodeVariable global : globals) { + appendOutput(source, "gl_FragData[" + i + "]", global); + i++; + } + } + } + unIndent(); + appendIndent(source); + source.append("}\n"); + } + + /** + * Appends an output assignment to a shader globalOutputName = + * nameSpace_varName; + * + * @param source the source StringBuilter to append the code. + * @param globalOutputName the name of the global output (can be gl_Position + * or gl_FragColor etc...). + * @param var the variable to assign to the output. + */ + protected void appendOutput(StringBuilder source, String globalOutputName, ShaderNodeVariable var) { + appendIndent(source); + source.append(globalOutputName); + source.append(" = "); + source.append(var.getNameSpace()); + source.append("_"); + source.append(var.getName()); + source.append(";\n"); + } + + /** + * {@inheritDoc} + * + * this methods does things in this order : + * + * 1. declaring and mapping input
+ * variables : variable replaced with MatParams or WorldParams that are Samplers are not + * declared and are replaced by the param actual name in the code. For others + * variables, the name space is appended with a "_" before the variable name + * in the code to avoid names collision between shaderNodes.
+ * + * 2. declaring output variables :
+ * variables are declared if they were not already + * declared as input (inputs can also be outputs) or if they are not + * declared as varyings. The variable name is also prefixed with the s=name + * space and "_" in the shaderNode code
+ * + * 3. append of the actual ShaderNode code
+ * + * 4. mapping outputs to global output if needed
+ * + *
+ * All of this is embed in a #if conditional statement if needed + */ + @Override + protected void generateNodeMainSection(StringBuilder source, ShaderNode shaderNode, String nodeSource, ShaderGenerationInfo info) { + + nodeSource = updateDefinesName(nodeSource, shaderNode); + source.append("\n"); + comment(source, shaderNode, "Begin"); + startCondition(shaderNode.getCondition(), source); + + final List declaredInputs = new ArrayList<>(); + + for (VariableMapping mapping : shaderNode.getInputMapping()) { + + final ShaderNodeVariable rightVariable = mapping.getRightVariable(); + final ShaderNodeVariable leftVariable = mapping.getLeftVariable(); + + //Variables fed with a sampler matparam or world param are replaced by the matparam itself + //It avoids issue with samplers that have to be uniforms. + if (isWorldOrMaterialParam(rightVariable) && rightVariable.getType().startsWith("sampler")) { + nodeSource = replace(nodeSource, leftVariable, rightVariable.getPrefix() + rightVariable.getName()); + } else { + + if (leftVariable.getType().startsWith("sampler")) { + throw new IllegalArgumentException("a Sampler must be a uniform"); + } + + map(mapping, source); + } + + String newName = shaderNode.getName() + "_" + leftVariable.getName(); + if (!declaredInputs.contains(newName)) { + nodeSource = replace(nodeSource, leftVariable, newName); + declaredInputs.add(newName); + } + } + + final ShaderNodeDefinition definition = shaderNode.getDefinition(); + + for (final ShaderNodeVariable var : definition.getInputs()) { + final ShaderNodeVariable variable = new ShaderNodeVariable(var.getType(), shaderNode.getName(), var.getName(), var.getMultiplicity()); + final String fullName = shaderNode.getName() + "_" + var.getName(); + if (!declaredInputs.contains(fullName)) { + if (!isVarying(info, variable)) { + declareVariable(source, variable); + } + nodeSource = replaceVariableName(nodeSource, variable); + declaredInputs.add(fullName); + } + } + + for (ShaderNodeVariable var : definition.getOutputs()) { + ShaderNodeVariable v = new ShaderNodeVariable(var.getType(), shaderNode.getName(), var.getName(), var.getMultiplicity()); + if (!declaredInputs.contains(shaderNode.getName() + "_" + var.getName())) { + if (!isVarying(info, v)) { + declareVariable(source, v); + } + nodeSource = replaceVariableName(nodeSource, v); + } + } + + source.append(nodeSource); + + for (VariableMapping mapping : shaderNode.getOutputMapping()) { + map(mapping, source); + } + endCondition(shaderNode.getCondition(), source); + comment(source, shaderNode, "End"); + } + + /** + * declares a variable, embed in a conditional block if needed + * @param source the StringBuilder to use + * @param var the variable to declare + * @param appendNameSpace true to append the nameSpace + "_" + */ + protected void declareVariable(StringBuilder source, ShaderNodeVariable var, boolean appendNameSpace) { + declareVariable(source, var, appendNameSpace, null); + } + + /** + * declares a variable, embed in a conditional block if needed. the namespace is appended with "_" + * @param source the StringBuilder to use + * @param var the variable to declare + */ + protected void declareVariable(StringBuilder source, ShaderNodeVariable var) { + declareVariable(source, var, true, null); + } + + /** + * declares a variable, embed in a conditional block if needed. the namespace is appended with "_" + * @param source the StringBuilder to use + * @param var the variable to declare + * @param value the initialization value to assign the the variable + */ + protected void declareVariable(StringBuilder source, ShaderNodeVariable var, String value) { + declareVariable(source, var, value, true, null); + } + + /** + * declares a variable, embed in a conditional block if needed. + * @param source the StringBuilder to use + * @param var the variable to declare + * @param appendNameSpace true to append the nameSpace + "_" + * @param modifier the modifier of the variable (attribute, varying, in , out,...) + */ + protected void declareVariable(StringBuilder source, ShaderNodeVariable var, boolean appendNameSpace, String modifier) { + declareVariable(source, var, null, appendNameSpace, modifier); + } + + /** + * declares a variable, embed in a conditional block if needed. + * @param source the StringBuilder to use + * @param var the variable to declare + * @param value the initialization value to assign the the variable + * @param appendNameSpace true to append the nameSpace + "_" + * @param modifier the modifier of the variable (attribute, varying, in , out,...) + */ + protected void declareVariable(StringBuilder source, ShaderNodeVariable var, String value, boolean appendNameSpace, String modifier) { + startCondition(var.getCondition(), source); + appendIndent(source); + if (modifier != null) { + source.append(modifier); + source.append(" "); + } + + source.append(var.getType()); + source.append(" "); + if (appendNameSpace) { + source.append(var.getNameSpace()); + source.append("_"); + } + source.append(var.getPrefix()); + source.append(var.getName()); + if (var.getMultiplicity() != null) { + source.append("["); + source.append(var.getMultiplicity().toUpperCase()); + source.append("]"); + } + if (value != null) { + source.append(" = "); + source.append(value); + } + source.append(";\n"); + endCondition(var.getCondition(), source); + } + + /** + * Starts a conditional block + * @param condition the block condition + * @param source the StringBuilder to use + */ + protected void startCondition(String condition, StringBuilder source) { + if (condition != null) { + appendIndent(source); + source.append("#if "); + source.append(condition); + source.append("\n"); + indent(); + } + } + + /** + * Ends a conditional block + * @param condition the block condition + * @param source the StringBuilder to use + */ + protected void endCondition(String condition, StringBuilder source) { + if (condition != null) { + unIndent(); + appendIndent(source); + source.append("#endif\n"); + + } + } + + /** + * Appends a mapping to the source, embed in a conditional block if needed, + * with variables nameSpaces and swizzle. + * @param mapping the VariableMapping to append + * @param source the StringBuilder to use + */ + protected void map(VariableMapping mapping, StringBuilder source) { + startCondition(mapping.getCondition(), source); + appendIndent(source); + if (!mapping.getLeftVariable().isShaderOutput()) { + source.append(mapping.getLeftVariable().getType()); + source.append(" "); + } + source.append(mapping.getLeftVariable().getNameSpace()); + source.append("_"); + source.append(mapping.getLeftVariable().getName()); + if (mapping.getLeftVariable().getMultiplicity() != null){ + source.append("["); + source.append(mapping.getLeftVariable().getMultiplicity()); + source.append("]"); + } + + //left swizzle, the variable can't be declared and assigned on the same line. + if (mapping.getLeftSwizzling().length() > 0) { + //initialize the declared variable to 0.0 + source.append(" = "); + source.append(mapping.getLeftVariable().getType()); + source.append("(0.0);\n"); + appendIndent(source); + //assign the value on a new line + source.append(mapping.getLeftVariable().getNameSpace()); + source.append("_"); + source.append(mapping.getLeftVariable().getName()); + source.append("."); + source.append(mapping.getLeftSwizzling()); + } + source.append(" = "); + String namePrefix = getAppendableNameSpace(mapping.getRightVariable()); + source.append(namePrefix); + source.append(mapping.getRightVariable().getPrefix()); + source.append(mapping.getRightVariable().getName()); + if (mapping.getRightSwizzling().length() > 0) { + source.append("."); + source.append(mapping.getRightSwizzling()); + } + source.append(";\n"); + endCondition(mapping.getCondition(), source); + } + + /** + * replaces a variable name in a shaderNode source code by prefixing it + * with its nameSpace and "_" if needed. + * @param nodeSource the source to modify + * @param var the variable to replace + * @return the modified source + */ + protected String replaceVariableName(String nodeSource, ShaderNodeVariable var) { + String namePrefix = getAppendableNameSpace(var); + String newName = namePrefix + var.getName(); + nodeSource = replace(nodeSource, var, newName); + return nodeSource; + } + + /** + * Finds if a variable is a varying + * @param info the ShaderGenerationInfo + * @param v the variable + * @return true is the given variable is a varying + */ + protected boolean isVarying(ShaderGenerationInfo info, ShaderNodeVariable v) { + boolean isVarying = false; + for (ShaderNodeVariable shaderNodeVariable : info.getVaryings()) { + if (shaderNodeVariable.equals(v)) { + isVarying = true; + } + } + return isVarying; + } + + /** + * Appends a comment to the generated code + * @param source the StringBuilder to use + * @param shaderNode the shader node being processed (to append its name) + * @param comment the comment to append + */ + protected void comment(StringBuilder source, ShaderNode shaderNode, String comment) { + appendIndent(source); + source.append("//"); + source.append(shaderNode.getName()); + source.append(" : "); + source.append(comment); + source.append("\n"); + } + + /** + * returns the name space to append for a variable. + * Attributes, WorldParam and MatParam names space must not be appended + * @param var the variable + * @return the namespace to append for this variable + */ + protected String getAppendableNameSpace(ShaderNodeVariable var) { + String namePrefix = var.getNameSpace() + "_"; + if (namePrefix.equals("Attr_") || namePrefix.equals("WorldParam_") || namePrefix.equals("MatParam_")) { + namePrefix = ""; + } + return namePrefix; + } + + /** + * transforms defines name is the shader node code. + * One can use a #if defined(inputVariableName) in a shaderNode code. + * This method is responsible for changing the variable name with the + * appropriate defined based on the mapping condition of this variable. + * Complex condition syntax are handled. + * + * @param nodeSource the sahderNode source code + * @param shaderNode the ShaderNode being processed + * @return the modified shaderNode source. + */ + protected String updateDefinesName(String nodeSource, ShaderNode shaderNode) { + String[] lines = nodeSource.split("\\n"); + ConditionParser parser = new ConditionParser(); + for (String line : lines) { + + if (line.trim().startsWith("#if")) { + List params = parser.extractDefines(line.trim()); + String l = line.trim().replaceAll("defined", "").replaceAll("#if ", "").replaceAll("#ifdef", "");//parser.getFormattedExpression(); + boolean match = false; + for (String param : params) { + for (VariableMapping map : shaderNode.getInputMapping()) { + if ((map.getLeftVariable().getName()).equals(param)) { + if (map.getCondition() != null) { + l = l.replaceAll(param, map.getCondition()); + match = true; + } + } + } + } + if (match) { + nodeSource = nodeSource.replace(line.trim(), "#if " + l); + } + } + } + return nodeSource; + } + + /** + * replaced a variable name in a source code with the given name + * @param nodeSource the source to use + * @param var the variable + * @param newName the new name of the variable + * @return the modified source code + */ + protected String replace(String nodeSource, ShaderNodeVariable var, String newName) { + nodeSource = nodeSource.replaceAll("(?<=\\W)" + var.getName() + "(?=\\W)", newName); + return nodeSource; + } + + /** + * Finds if a variable is a world or a material parameter + * @param var the variable + * @return true if the variable is a Word or material parameter + */ + protected boolean isWorldOrMaterialParam(ShaderNodeVariable var) { + return var.getNameSpace().equals("MatParam") || var.getNameSpace().equals("WorldParam"); + } + + @Override + protected String getLanguageAndVersion(ShaderType type) { + return "GLSL100"; + } + + /** + * appends indentation. + * @param source + */ + protected void appendIndent(StringBuilder source) { + source.append(getIndent(indent)); + } + + /** + * Declares an attribute + * @param source the StringBuilder to use + * @param var the variable to declare as an attribute + */ + protected void declareAttribute(StringBuilder source, ShaderNodeVariable var) { + declareVariable(source, var, false, "attribute"); + } + + /** + * Declares a varying + * @param source the StringBuilder to use + * @param var the variable to declare as an varying + * @param input a boolean set to true if the this varying is an input. + * this in not used in this implementation but can be used in overridings + * implementation + */ + protected void declareVarying(StringBuilder source, ShaderNodeVariable var, boolean input) { + declareVariable(source, var, true, "varying"); + } + + /** + * Decrease indentation with a check so the indent is never negative. + */ + protected void unIndent() { + indent--; + indent = Math.max(0, indent); + } + + /** + * increase indentation with a check so that indentation is never over 10 + */ + protected void indent() { + indent++; + indent = Math.min(10, indent); + } + + /** + * makes sure inPosition attribute is of type vec3 or vec4 + * @param var the inPosition attribute + */ + private void fixInPositionType(ShaderNodeVariable var) { + if(!var.getType().equals("vec3") || !var.getType().equals("vec4")){ + var.setType("vec3"); + } + } + + /** + * declare and assign the global position in the vertex shader. + * @param info the shader generation info + * @param source the shader source being generated + */ + protected void declareGlobalPosition(ShaderGenerationInfo info, StringBuilder source) { + if(inPosTmp.getType().equals(info.getVertexGlobal().getType())){ + declareVariable(source, info.getVertexGlobal(), "inPosition"); + }else{ + declareVariable(source, info.getVertexGlobal(), "vec4(inPosition,1.0)"); + } + } +} diff --git a/src/main/java/com/jme3/shader/Glsl150ShaderGenerator.java b/src/main/java/com/jme3/shader/Glsl150ShaderGenerator.java new file mode 100644 index 00000000..4572387c --- /dev/null +++ b/src/main/java/com/jme3/shader/Glsl150ShaderGenerator.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2009-2012 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.shader; + +import com.jme3.asset.AssetManager; +import com.jme3.material.ShaderGenerationInfo; +import com.jme3.shader.Shader.ShaderType; + +/** + * This shader Generator can generate Vertex and Fragment shaders from + * ShaderNodes for GLSL 1.5 + * + * @author Nehon + */ +public class Glsl150ShaderGenerator extends Glsl100ShaderGenerator { + + /** + * Creates a Glsl150ShaderGenerator + * + * @param assetManager the assetmanager + */ + public Glsl150ShaderGenerator(AssetManager assetManager) { + super(assetManager); + } + + @Override + protected String getLanguageAndVersion(ShaderType type) { + return "GLSL150"; + } + + /** + * {@inheritDoc} in glsl 1.5 attributes are prefixed with the "in" keyword + * and not the "attribute" keyword + */ + @Override + protected void declareAttribute(StringBuilder source, ShaderNodeVariable var) { + declareVariable(source, var, false, "in"); + } + + /** + * {@inheritDoc} in glsl 1.5 varying are prefixed with the "in" or "out" + * keyword and not the "varying" keyword. + * + * "in" is used for Fragment shader (maybe Geometry shader later) "out" is + * used for Vertex shader (maybe Geometry shader later) + */ + @Override + protected void declareVarying(StringBuilder source, ShaderNodeVariable var, boolean input) { + declareVariable(source, var, true, input ? "in" : "out"); + } + + @Override + protected void generateUniforms(StringBuilder source, ShaderGenerationInfo info, ShaderType type) { + generateCompatibilityDefines(source, type); + super.generateUniforms(source, info, type); + } + + private void generateCompatibilityDefines(StringBuilder source, ShaderType type) { + //Adding compatibility defines, as it's more efficient than replacing the function calls in the source code + if (type == ShaderType.Fragment) { + source.append("\n") + .append("#define texture1D texture\n") + .append("#define texture2D texture\n") + .append("#define texture3D texture\n") + .append("#define textureCube texture\n") + .append("#define texture2DLod textureLod\n") + .append("#define textureCubeLod textureLod\n"); + } + } + + /** + * {@inheritDoc} + * + * Fragment shader outputs are declared before the "void main(){" with the + * "out" keyword. + * + * after the "void main(){", the vertex output are declared and initialized + * and the fragment outputs are declared + */ + @Override + protected void generateStartOfMainSection(StringBuilder source, ShaderGenerationInfo info, ShaderType type) { + source.append("\n"); + + if (type == ShaderType.Fragment) { + for (ShaderNodeVariable global : info.getFragmentGlobals()) { + declareVariable(source, global, null, true, "out"); + } + } + source.append("\n"); + + appendIndent(source); + source.append("void main() {\n"); + indent(); + + if (type == ShaderType.Vertex) { + declareGlobalPosition(info, source); + } else if (type == ShaderType.Fragment) { + for (ShaderNodeVariable global : info.getFragmentGlobals()) { + initVariable(source, global, "vec4(1.0)"); + } + } + } + + /** + * {@inheritDoc} + * + * only vertex shader output are mapped here, since fragment shader outputs + * must have been mapped in the main section. + */ + @Override + protected void generateEndOfMainSection(StringBuilder source, ShaderGenerationInfo info, ShaderType type) { + if (type == ShaderType.Vertex) { + appendOutput(source, "gl_Position", info.getVertexGlobal()); + } + unIndent(); + appendIndent(source); + source.append("}\n"); + } + + /** + * Append a variable initialization to the code + * + * @param source the StringBuilder to use + * @param var the variable to initialize + * @param initValue the init value to assign to the variable + */ + protected void initVariable(StringBuilder source, ShaderNodeVariable var, String initValue) { + appendIndent(source); + source.append(var.getNameSpace()); + source.append("_"); + source.append(var.getName()); + source.append(" = "); + source.append(initValue); + source.append(";\n"); + } +} diff --git a/src/main/java/com/jme3/shader/ShaderGenerator.java b/src/main/java/com/jme3/shader/ShaderGenerator.java new file mode 100644 index 00000000..bfa46aba --- /dev/null +++ b/src/main/java/com/jme3/shader/ShaderGenerator.java @@ -0,0 +1,369 @@ +/* + * Copyright (c) 2009-2012 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.shader; + +import com.jme3.asset.AssetKey; +import com.jme3.asset.AssetManager; +import com.jme3.material.ShaderGenerationInfo; +import com.jme3.material.TechniqueDef; +import com.jme3.shader.Shader.ShaderType; + +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * This class is the base for a shader generator using the ShaderNodes system, + * it contains basis mechanism of generation, but no actual generation code. + * This class is abstract, any Shader generator must extend it. + * + * @author Nehon + */ +public abstract class ShaderGenerator { + + protected static final char[] EMPTY_CHARS = new char[0]; + + /** + * Calculate the indent using space characters. + * + * @param level the level. + * @return the result indent. + */ + protected static char[] getIndent(final int level) { + + if (level == 0) { + return EMPTY_CHARS; + } + + final int characters = level * 4; + final char[] result = new char[characters]; + + for (int i = 0; i < result.length; i++) { + result[i] = ' '; + } + + return result; + } + + /** + * the asset manager + */ + protected AssetManager assetManager; + /** + * indentation value for generation + */ + protected int indent; + /** + * the technique def to use for the shader generation + */ + protected TechniqueDef techniqueDef = null; + /** + * Extension pattern + */ + Pattern extensions = Pattern.compile("(#extension.*\\s+)"); + + /** + * Build a shaderGenerator + * + * @param assetManager + */ + protected ShaderGenerator(AssetManager assetManager) { + this.assetManager = assetManager; + } + + public void initialize(TechniqueDef techniqueDef){ + this.techniqueDef = techniqueDef; + } + + /** + * Generate vertex and fragment shaders for the given technique + * + * @return a Shader program + */ + public Shader generateShader(String definesSourceCode) { + if (techniqueDef == null) { + throw new UnsupportedOperationException("The shaderGenerator was not " + + "properly initialized, call " + + "initialize(TechniqueDef) before any generation"); + } + + String techniqueName = techniqueDef.getName(); + ShaderGenerationInfo info = techniqueDef.getShaderGenerationInfo(); + + Shader shader = new Shader(); + for (ShaderType type : ShaderType.values()) { + String extension = type.getExtension(); + String language = getLanguageAndVersion(type); + String shaderSourceCode = buildShader(techniqueDef.getShaderNodes(), info, type); + + if (shaderSourceCode != null) { + String shaderSourceAssetName = techniqueName + "." + extension; + shader.addSource(type, shaderSourceAssetName, shaderSourceCode, definesSourceCode, language); + } + } + + techniqueDef = null; + return shader; + } + + /** + * This method is responsible for the shader generation. + * + * @param shaderNodes the list of shader nodes + * @param info the ShaderGenerationInfo filled during the Technique loading + * @param type the type of shader to generate + * @return the code of the generated vertex shader + */ + protected String buildShader(List shaderNodes, ShaderGenerationInfo info, ShaderType type) { + if (type == ShaderType.TessellationControl || + type == ShaderType.TessellationEvaluation || + type == ShaderType.Geometry) { + // TODO: Those are not supported. + // Too much code assumes that type is either Vertex or Fragment + return null; + } + + indent = 0; + + StringBuilder sourceDeclaration = new StringBuilder(); + StringBuilder source = new StringBuilder(); + + generateUniforms(sourceDeclaration, info, type); + + if (type == ShaderType.Vertex) { + generateAttributes(sourceDeclaration, info); + } + generateVaryings(sourceDeclaration, info, type); + + generateStartOfMainSection(source, info, type); + + generateDeclarationAndMainBody(shaderNodes, sourceDeclaration, source, info, type); + + generateEndOfMainSection(source, info, type); + + sourceDeclaration.append(source); + + return moveExtensionsUp(sourceDeclaration); + } + + /** + * parses the source and moves all the extensions at the top of the shader source as having extension declarations + * in the middle of a shader is against the specs and not supported by all drivers. + * @param sourceDeclaration + * @return + */ + private String moveExtensionsUp(StringBuilder sourceDeclaration) { + Matcher m = extensions.matcher( sourceDeclaration.toString()); + StringBuilder finalSource = new StringBuilder(); + while(m.find()){ + finalSource.append(m.group()); + } + finalSource.append(m.replaceAll("")); + return finalSource.toString(); + } + + /** + * iterates through shader nodes to load them and generate the shader + * declaration part and main body extracted from the shader nodes, for the + * given shader type + * + * @param shaderNodes the list of shader nodes + * @param sourceDeclaration the declaration part StringBuilder of the shader + * to generate + * @param source the main part StringBuilder of the shader to generate + * @param info the ShaderGenerationInfo + * @param type the Shader type + */ + protected void generateDeclarationAndMainBody(List shaderNodes, StringBuilder sourceDeclaration, StringBuilder source, ShaderGenerationInfo info, ShaderType type) { + for (ShaderNode shaderNode : shaderNodes) { + if (info.getUnusedNodes().contains(shaderNode.getName())) { + continue; + } + if (shaderNode.getDefinition().getType() == type) { + int index = findShaderIndexFromVersion(shaderNode, type); + String shaderPath = shaderNode.getDefinition().getShadersPath().get(index); + String loadedSource = (String) assetManager.loadAsset(new AssetKey(shaderPath)); + appendNodeDeclarationAndMain(loadedSource, sourceDeclaration, source, shaderNode, info, shaderPath); + } + } + } + + /** + * Appends declaration and main part of a node to the shader declaration and + * main part. the loadedSource is split by "void main(){" to split + * declaration from main part of the node source code.The trailing "}" is + * removed from the main part. Each part is then respectively passed to + * generateDeclarativeSection and generateNodeMainSection. + * + * @see ShaderGenerator#generateDeclarativeSection + * @see ShaderGenerator#generateNodeMainSection + * + * @param loadedSource the actual source code loaded for this node. + * @param shaderPath path the the shader file + * @param sourceDeclaration the Shader declaration part string builder. + * @param source the Shader main part StringBuilder. + * @param shaderNode the shader node. + * @param info the ShaderGenerationInfo. + */ + protected void appendNodeDeclarationAndMain(String loadedSource, StringBuilder sourceDeclaration, StringBuilder source, ShaderNode shaderNode, ShaderGenerationInfo info, String shaderPath) { + if (loadedSource.length() > 1) { + loadedSource = loadedSource.substring(0, loadedSource.lastIndexOf("}")); + String[] sourceParts = loadedSource.split("\\s*void\\s*main\\s*\\(\\s*\\)\\s*\\{"); + if(sourceParts.length<2){ + throw new IllegalArgumentException("Syntax error in "+ shaderPath +". Cannot find 'void main(){' in \n"+ loadedSource); + } + generateDeclarativeSection(sourceDeclaration, shaderNode, sourceParts[0], info); + generateNodeMainSection(source, shaderNode, sourceParts[1], info); + } else { + //if source is empty, we still call generateNodeMainSection so that mappings can be done. + generateNodeMainSection(source, shaderNode, loadedSource, info); + } + + } + + /** + * returns the language + version of the shader should be something like + * "GLSL100" for glsl 1.0 "GLSL150" for glsl 1.5. + * + * @param type the shader type for which the version should be returned. + * + * @return the shaderLanguage and version. + */ + protected abstract String getLanguageAndVersion(ShaderType type); + + /** + * generates the uniforms declaration for a shader of the given type. + * + * @param source the source StringBuilder to append generated code. + * @param info the ShaderGenerationInfo. + * @param type the shader type the uniforms have to be generated for. + */ + protected abstract void generateUniforms(StringBuilder source, ShaderGenerationInfo info, ShaderType type); + + /** + * generates the attributes declaration for the vertex shader. There is no + * Shader type passed here as attributes are only used in vertex shaders + * + * @param source the source StringBuilder to append generated code. + * @param info the ShaderGenerationInfo. + */ + protected abstract void generateAttributes(StringBuilder source, ShaderGenerationInfo info); + + /** + * generates the varyings for the given shader type shader. Note that + * varyings are deprecated in glsl 1.3, but this method will still be called + * to generate all non global inputs and output of the shaders. + * + * @param source the source StringBuilder to append generated code. + * @param info the ShaderGenerationInfo. + * @param type the shader type the varyings have to be generated for. + */ + protected abstract void generateVaryings(StringBuilder source, ShaderGenerationInfo info, ShaderType type); + + /** + * Appends the given shaderNode declarative part to the shader declarative + * part. If needed the sahder type can be determined by fetching the + * shaderNode's definition type. + * + * @see ShaderNode#getDefinition() + * @see ShaderNodeDefinition#getType() + * + * @param nodeDecalarationSource the declaration part of the node + * @param source the StringBuilder to append generated code. + * @param shaderNode the shaderNode. + * @param info the ShaderGenerationInfo. + */ + protected abstract void generateDeclarativeSection(StringBuilder source, ShaderNode shaderNode, String nodeDecalarationSource, ShaderGenerationInfo info); + + /** + * generates the start of the shader main section. this method is + * responsible of appending the "void main(){" in the shader and declaring + * all global outputs of the shader + * + * @param source the StringBuilder to append generated code. + * @param info the ShaderGenerationInfo. + * @param type the shader type the section has to be generated for. + */ + protected abstract void generateStartOfMainSection(StringBuilder source, ShaderGenerationInfo info, ShaderType type); + + /** + * generates the end of the shader main section. this method is responsible + * of appending the last "}" in the shader and mapping all global outputs of + * the shader + * + * @param source the StringBuilder to append generated code. + * @param info the ShaderGenerationInfo. + * @param type the shader type the section has to be generated for. + */ + protected abstract void generateEndOfMainSection(StringBuilder source, ShaderGenerationInfo info, ShaderType type); + + /** + * Appends the given shaderNode main part to the shader declarative part. If + * needed the shader type can be determined by fetching the shaderNode's + * definition type. + * + * @see ShaderNode#getDefinition() + * @see ShaderNodeDefinition#getType() + * + * @param source the StringBuilder to append generated code. + * @param shaderNode the shaderNode. + * @param nodeSource the declaration part of the loaded shaderNode source. + * @param info the ShaderGenerationInfo. + */ + protected abstract void generateNodeMainSection(StringBuilder source, ShaderNode shaderNode, String nodeSource, ShaderGenerationInfo info); + + /** + * returns the shaderpath index according to the version of the generator. + * This allow to select the higher version of the shader that the generator + * can handle + * + * @param shaderNode the shaderNode being processed + * @param type the shaderType + * @return the index of the shader path in ShaderNodeDefinition shadersPath + * list + * @throws NumberFormatException + */ + protected int findShaderIndexFromVersion(ShaderNode shaderNode, ShaderType type) throws NumberFormatException { + int index = 0; + List lang = shaderNode.getDefinition().getShadersLanguage(); + int genVersion = Integer.parseInt(getLanguageAndVersion(type).substring(4)); + int curVersion = 0; + for (int i = 0; i < lang.size(); i++) { + int version = Integer.parseInt(lang.get(i).substring(4)); + if (version > curVersion && version <= genVersion) { + curVersion = version; + index = i; + } + } + return index; + } +} From 3ba2a9c82f14001b0ec7691e2be208254440e20f Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Mon, 18 Sep 2017 08:07:40 +0300 Subject: [PATCH 24/50] fixed reusing file icons. --- .../ss/editor/manager/FileIconManager.java | 52 ++++++++++--------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/src/main/java/com/ss/editor/manager/FileIconManager.java b/src/main/java/com/ss/editor/manager/FileIconManager.java index e50a1244..aeff8f69 100644 --- a/src/main/java/com/ss/editor/manager/FileIconManager.java +++ b/src/main/java/com/ss/editor/manager/FileIconManager.java @@ -190,7 +190,7 @@ public void register(@NotNull final BiFunction iconFinder) public @NotNull Image getIcon(@NotNull final Path path, final boolean directory, final boolean tryToGetContentType, int size) { - final String extension = FileUtils.getExtension(path); + final String extension = directory ? "folder" : FileUtils.getExtension(path); final Array> iconFinders = getIconFinders(); if (!iconFinders.isEmpty()) { @@ -203,10 +203,16 @@ public void register(@NotNull final BiFunction iconFinder) continue; } - return buildImage(url, EditorUtil.getInputStream(url, classLoader), size); + return getImage(url, classLoader, size); } } + String url = extensionToUrl.get(extension); + + if (url != null) { + return getImage(url, size); + } + String contentType; if (directory) { @@ -233,39 +239,35 @@ public void register(@NotNull final BiFunction iconFinder) contentType = "none"; } - String url = extensionToUrl.get(extension); + for (final Path mimeTypes : MIME_TYPES_FOLDERS) { - if (url == null) { - for (final Path mimeTypes : MIME_TYPES_FOLDERS) { + Path iconPath = mimeTypes.resolve(contentType + ".svg"); + url = toAssetPath(iconPath); - Path iconPath = mimeTypes.resolve(contentType + ".svg"); + if (!EditorUtil.checkExists(url)) { + contentType = EXTENSION_TO_CONTENT_TYPE.get(extension); + iconPath = mimeTypes.resolve(contentType + ".svg"); url = toAssetPath(iconPath); + } - if (!EditorUtil.checkExists(url)) { - contentType = EXTENSION_TO_CONTENT_TYPE.get(extension); - iconPath = mimeTypes.resolve(contentType + ".svg"); - url = toAssetPath(iconPath); - } - - if (!EditorUtil.checkExists(url)) { - contentType = EXTENSION_TO_CONTENT_TYPE.get(extension); - iconPath = mimeTypes.resolve(contentType + ".png"); - url = toAssetPath(iconPath); - } - - if (EditorUtil.checkExists(url)) { - break; - } + if (!EditorUtil.checkExists(url)) { + contentType = EXTENSION_TO_CONTENT_TYPE.get(extension); + iconPath = mimeTypes.resolve(contentType + ".png"); + url = toAssetPath(iconPath); } - if (url == null || !EditorUtil.checkExists(url)) { - LOGGER.warning("not found image for contentType " + contentType + " and path " + path); - url = "/ui/icons/svg/document.svg"; + if (EditorUtil.checkExists(url)) { + break; } + } - extensionToUrl.put(extension, url); + if (url == null || !EditorUtil.checkExists(url)) { + LOGGER.warning("not found image for contentType " + contentType + " and path " + path); + url = "/ui/icons/svg/document.svg"; } + extensionToUrl.put(extension, url); + return getImage(url, size); } From 991a8243a4c433b94a961c52573d126a6f71d3df Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Mon, 18 Sep 2017 08:32:45 +0300 Subject: [PATCH 25/50] updated API of CssRegistry. --- .../com/ss/editor/ui/css/CSSRegistry.java | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/ss/editor/ui/css/CSSRegistry.java b/src/main/java/com/ss/editor/ui/css/CSSRegistry.java index 4964c6b7..36071e93 100644 --- a/src/main/java/com/ss/editor/ui/css/CSSRegistry.java +++ b/src/main/java/com/ss/editor/ui/css/CSSRegistry.java @@ -1,10 +1,13 @@ package com.ss.editor.ui.css; +import static com.ss.rlib.util.ObjectUtils.notNull; import com.ss.editor.annotation.FromAnyThread; import com.ss.rlib.util.array.Array; import com.ss.rlib.util.array.ArrayFactory; import org.jetbrains.annotations.NotNull; +import java.net.URL; + /** * The registry of available css files. * @@ -33,11 +36,22 @@ private CSSRegistry() { /** * Add the CSS file to this registry. * - * @param cssFile the CSS file. + * @param cssFile the URL to the CSS file. + */ + @FromAnyThread + public void register(@NotNull final URL cssFile) { + availableCssFiles.add(cssFile.toExternalForm()); + } + + /** + * Add the CSS file to this registry. + * + * @param cssFile the path to CSS file. + * @param classLoader the class loader which can load this path. */ @FromAnyThread - public void register(@NotNull String cssFile) { - availableCssFiles.add(cssFile); + public void register(@NotNull final String cssFile, @NotNull final ClassLoader classLoader) { + register(notNull(classLoader.getResource(cssFile))); } /** From 10f7f4450606d8897b54d259f4f2433ee1a863c1 Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Mon, 18 Sep 2017 08:41:29 +0300 Subject: [PATCH 26/50] compilation fix. --- .../editor/ui/builder/EditorFXSceneBuilder.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/ss/editor/ui/builder/EditorFXSceneBuilder.java b/src/main/java/com/ss/editor/ui/builder/EditorFXSceneBuilder.java index a274eb9b..283e4c99 100644 --- a/src/main/java/com/ss/editor/ui/builder/EditorFXSceneBuilder.java +++ b/src/main/java/com/ss/editor/ui/builder/EditorFXSceneBuilder.java @@ -45,31 +45,31 @@ public class EditorFXSceneBuilder { * The path to the base CSS styles. */ @NotNull - public static final String CSS_FILE_BASE = "/ui/css/base.css"; + public static final String CSS_FILE_BASE = "ui/css/base.css"; /** * The path to the external CSS styles. */ @NotNull - public static final String CSS_FILE_EXTERNAL = "/ui/css/external.css"; + public static final String CSS_FILE_EXTERNAL = "ui/css/external.css"; /** * The path to the custom ids CSS styles. */ @NotNull - public static final String CSS_FILE_CUSTOM_IDS = "/ui/css/custom_ids.css"; + public static final String CSS_FILE_CUSTOM_IDS = "ui/css/custom_ids.css"; /** * The path to the custom classes CSS styles. */ @NotNull - public static final String CSS_FILE_CUSTOM_CLASSES = "/ui/css/custom_classes.css"; + public static final String CSS_FILE_CUSTOM_CLASSES = "ui/css/custom_classes.css"; static { - CSS_REGISTRY.register(CSS_FILE_BASE); - CSS_REGISTRY.register(CSS_FILE_EXTERNAL); - CSS_REGISTRY.register(CSS_FILE_CUSTOM_IDS); - CSS_REGISTRY.register(CSS_FILE_CUSTOM_CLASSES); + CSS_REGISTRY.register(CSS_FILE_BASE, EditorFXSceneBuilder.class.getClassLoader()); + CSS_REGISTRY.register(CSS_FILE_EXTERNAL, EditorFXSceneBuilder.class.getClassLoader()); + CSS_REGISTRY.register(CSS_FILE_CUSTOM_IDS, EditorFXSceneBuilder.class.getClassLoader()); + CSS_REGISTRY.register(CSS_FILE_CUSTOM_CLASSES, EditorFXSceneBuilder.class.getClassLoader()); } /** From 9470a49444ec2d22ed8ef6c61e5d16d958fe241f Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Mon, 18 Sep 2017 20:19:29 +0300 Subject: [PATCH 27/50] updated tree generator plugin with fix of exporting trees with wires. --- build.gradle | 3 +-- .../ss-editor-font-generator-1.0.2.jar | Bin 13398 -> 0 bytes .../ss-editor-font-generator-1.0.3.jar | Bin 0 -> 13361 bytes ...jar => ss-editor-tree-generator-1.0.3.jar} | Bin 83254 -> 84017 bytes 4 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 embedded-plugins/ss-editor-font-generator/ss-editor-font-generator-1.0.2.jar create mode 100644 embedded-plugins/ss-editor-font-generator/ss-editor-font-generator-1.0.3.jar rename embedded-plugins/ss-editor-tree-generator/{ss-editor-tree-generator-1.0.2.jar => ss-editor-tree-generator-1.0.3.jar} (61%) diff --git a/build.gradle b/build.gradle index 943110ce..99605877 100644 --- a/build.gradle +++ b/build.gradle @@ -41,7 +41,6 @@ junitPlatform { logManager 'org.apache.logging.log4j.jul.LogManager' } - repositories { mavenCentral() jcenter() @@ -129,7 +128,7 @@ dependencies { compile ('com.github.JavaSaBr:jme3-spaceshift-extension:1.6.0') { exclude group: 'org.jmonkeyengine' } - compile ('com.github.JavaSaBr:tonegodemitter:2.3.4') { + compile ('com.github.JavaSaBr:tonegodemitter:develop-SNAPSHOT') { exclude group: 'org.jmonkeyengine' } diff --git a/embedded-plugins/ss-editor-font-generator/ss-editor-font-generator-1.0.2.jar b/embedded-plugins/ss-editor-font-generator/ss-editor-font-generator-1.0.2.jar deleted file mode 100644 index 11c982e0d9903da4429f18a0610edd1fc6ccb479..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13398 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

MUP2rhsbti`{l-nSyFJat>)|NY2^fH5ei6K zuP@3ctxW1CY>*!YEaKXHTb?%xB!A_I%7it*fAzDUlS8jaBZ3|U{r5~&iC4@ zy49oA4bc)~kf!W2y2PDwAVLZDC~7-iL%kyk?0n6$L8`C`N5-OL$V_%gM#egt8p@K+ z73aiZjti-=3*^YJNtqJK%wvct)u+5aGCGBypBk)X!mk%ZQqV!+b|;}KH|2l5&qz3Q5c{%GVMKd>4g+% zHS4PU=E3YSt;&9{71qGziJo^#k8{-<;{Trq$^R*dS^saT^B3Li&s6AEH;=l<`@#I5pVzeV~;g4B%->C7*h1?8-6!!Lh9WowJs7OfHRA*tPw83G}}Vr$9Ojg z{SbT*y`M2&trf;SOi2B{GT-<&MNfhRJX!CJWd!&f77YCKvnt9iNc$WwA|BPKlfM6S{%JYBlrQanV1E+A|H|;w`*)T7PrdtZ zh645|@bjSmseb=E#8376Z}tUin?FX(@j@qdT?sf_>4 zeqv8vx_`y~k52y21iy+pf9m9av!4XSFWdUR5d5K<|CQ=j)%@>dHNJZPMD-6E`d=gc zWd8k!`Kd(x&3+Er5&9nn{ktypSIS>?slSuf_%{Aq%D+*m{u=Gq$I3sV0bfeczg=~i zzdU6AnfSjIvwy6N^t*o`{;lr&mGIZn_K%T9{yW0|6uQ4M|5}v(!Awu_uZH@2iTW$? zPom#<$IpZ8H~YCelwXSTzg^tl6Z2p0(f@X={mxqBoAj?ae<%M_%KvrfzaA%lgbAVj gw?qFu%wG=|c`2xueGCSM`tnhM0s}js`}ytv0f7L%D*ylh diff --git a/embedded-plugins/ss-editor-font-generator/ss-editor-font-generator-1.0.3.jar b/embedded-plugins/ss-editor-font-generator/ss-editor-font-generator-1.0.3.jar new file mode 100644 index 0000000000000000000000000000000000000000..ca83fe4b491bb39e6f673f4a9a75078e9d6dc803 GIT binary patch literal 13361 zcma)j19)WH(sm}EaAMoZ#I|kQwylXZv2EKE+qP{?GO?3CbI+ZN`R;$tsqVdd_w!V} zwQBWRRkgd{mJtU6h5!Hn2L}+)q7w&r7r>vVclr2!ND3lYjqrc8jjXL4{?r8Pf14N^S{d5u z{x5&KU{R8-?-7bY0swIQm9fyfd*NSDg8v9W#=_Cq)QU>qLf76tUCB}zL-CUrl?I_)6Y(}ts;%jNtYK zQgZO=f@>4~VC-2ub}PPg*t0dvSD-3OL?sSm_eflUQI>2e^DItb4V-E%1ssd7uq&iQ zrLt~_PoR{QX`zc^6#h)UXI4R!OuI_j>@~9v*Sv3E44CSywM?X(xE#TswBSZd?!L~O z)&(Xf+SX}9uUXbY$qsI@5zV+_zbsjN8vcVf+EfqDi%Q-Qb+83G)Vft9MXp5=!7Biv zPIRQ)zHr#nbXSEntC8qpbwwBjx>$2#9bcq{3_KRYHoiCLXfi=@ z+F6J#svG~|YFXQHOSxdZ@)B_&X!81Ty`^nmDn1|8tY$s(kgfwkO>Wi+S2jtHYEYvY zG*_E;{t;{7(@~LMyu)|O%PQt?>*H8w$D^|0T$|CfJrB9KsDXXfYDHhB^P@@4wY(ep z+rA~1)vVl}boUP0+=O$+GIl~GZl6?Q({eRN9%ogE3G z$S&0sgx6NCB;i$4Jd@tiH`dxOvB&X?mk3UjIvpKBEAq-xOpl(HmFGzi7YAX*MGFNb zZFT99L(t@}DU1@yg#xZ2k?mOdg3B>t2MY}=7VAV-6eF3|ib`y2z$^do0_-E4ml4xh z8^nM@5U(gLKKa&2eBH1D-Vb&$2F!FMo6#u&kK~ccGpFJ`K;zx~!RT;9j?HI4)FYAv zs&=3|2^-o%k~0e0DHTc{tb8YoE!g`_rT0ZwJ*K&j6TxU_W-PT>?fwJz^th^}TZ5_j z(W1JgBCN2UXhpxUnB%^{ir<#oqj%P*QOcnv{B$@giPSSUDIUMWE_V<<<>{6;`(c(H`Rwn`vlVY6fF!Hmc1lQ6* zP82byg53Qg$336=k61+>Ke}b)PkdTr^`8bXH3fvLTThq<%f&wm`_yZdc?NpaWU$2vThNMeR%cueK0xg4Yl-|tc(dbR!4HC-X zu%8rjlXxnhELs9(@6dc$R-=y*a7~&Lsn7}YMM0tiaYntCIs}NCW--+)!Dhe3rF@|k zIexQ$$d%+XJ&*%a>t2$xg`LSQNTBz?X5LL2CyAuD^18jhW9nu5N*{M1Y!NoNwCQlV ztm0P`aXGQZ?oHzhJWfebuAID@4;G^!z}L24*uP8!vWi}! z_}>yCS6}1&yO#V+?Jv2hXIel207~Eh0JMLV75_+ql7{y7y2gg~f26_yg(0^fC zdRUA~Wj|br)m})y zq^$%vAD?_{Hw21^sC0zXWqgn{VZ|ibsqJEui_q+erR1q(Cxy)+2^X2rw4S!?F3UW6= zAcbbeR~Vwe!hCsz?9&$8R!=X1GHr&r1Y%pLTD_iZkRCPDob#i6G-A+GN7@;~&+^`S z5IA8Lpir5?F7h6PRjzqUbdE-W+fD^~l=8MD^0LDUWya)ZM&wL%NO$i~dZbh|2-@4M zO?o(PlqGIHaqkXf72RWL4L84|4O&f?bJ)>V-iWNYZdKn0#CB;JsVy*IZiy|-t`V|U zzN)BhS6@gn4MG>jfCX@>e4At)+5n1<{6cr!SMEv{d+SDOE(iG#l4~$~0=^idq3}U2GCa>}GB*oRR zKgfkj&g4PoiAwOTAQGiZv=aiwCezTH%9~{KkQK>blT*X-PLk*Fm6~RUZFBKxs;X+2 zg_X7_AtH7SK!l3id7s@$(#1;548M8>?x#Mjxt}#1WDLBexs!ODq?2@r2^bfHabCM> zkwsoXYwcx!)=CzC@bC%R7%Amk9We@Br0{Ucg3az-C z+uC)_$;sTIX<37N!?z=(ht}S)K#%qf$qu@Epo)2n$QRLdYR8#U?6aM6>?#ev@8Ds^aaa{)CtSYg-M9%UPe$L;CFs0CWrHR8zcITf~k z&Mrj(m2m@#R_jOmn)v;5pxQh8YkYR)pB>V>7TjH6H==~oTNgGlIAtVO!bTTK%GLL5Yg^ZietZYr*xHKxA(f)jLu;_>-g z(D{z^VA>Psv#3{R5A3C8&7Cybb5s!ia}@`dFD(`h@^WgC zld$Q>7!K^%fgNnFPO!ZTL*08MSjxSRez2}4%s2@H2-sfmYP$r_u+wg0YTEJ1+9Ua3 z52mJ`nrd%JBX7NLr(51z#sP z5AIg4?Wx|I`#8-HAk1$;sosP$uQQt{oSTpOwi9o3_axYEi%y%Fp_H%cT5kj5y&ZA$ zBcL7K22Ku4?dRM3?UEmBOuXk0D3x_t&^G@bF0L!o>uy%6?^0i%2Sd0a;I z$Y&AMm>DX}&rYn?d|wbHYHZjbTnM12>I4Y+s6Wvf}sUl!YC{jlV zhgPW`=jl}=i`fV50%xz)lb9n(!R3vn+KFR3R%5z@;`jX^yL_E>2-OQnpwu0jFBr&b zK%`b^A*)$?FEBqCrZV5T3zL09DZyA9j2ITdAmk)5tQZKwu0JJ9k+4`jkB%6-6f-zY zVzZT&goFc0(n+}H5AHnh6+?P+@klQsa3GK`y1CP&$Z8g@*s8!{`GGl;r}+)c{H}qA zg#n0BvBk|Sq*~CrH=V8= z*%j-Q0!*$Bb~tdYc08JreBp9^8nJNPnxd|bhL>7HGxk~v@cd~QcQI+w4;@wg)!*d? z@(Z)wqiKYGFh!D-iC{K#!sr%fAw@cCz;p;L(mtFfXOmi>gc36K@de{Z7*RNu)#ti$ zm=I-`Thw&uf2L&v9ol*zpumD2iHRC22>D|23Cl6vr?;x0Qyf?pLKz3T_j7fMC4sw| z*Po)XiQexru&J{OXpZ=`p?$fYiQ@&`#`117Vx>KbQ|Bg<7qYd_*44Q3EL%m#fd%oa z^s-2T=7Ga8NLWDYNGO0pA5wkhXl6rd!VzH&8QPK#uXlIbXU?oNuc)Se_FU8nv<5!@ z9-Z&YKF;^VCA@y#`ajHz_MyD6q>3!#V$hS7jH_mCEx>jR9V`iGswh8wREywWA|HhY zqQfe>nMkS^+MTRO$MPS7S5tXzf#Vp54NUJnK8lFw^NpZaGLY~sgfx!L$TpUAi+Bw$ zwrg-}d(|$eU58FUe-@72*kp!&jUDpdZ-jac8?v6^WqE6v zI9mMpHn-_4!BsG*FlwDFHpR?HXD|%vo8yCvzoHPJGu}a`7){P}1noAXprzugCt zff|H;vEi|U{M;5VcXnnsbKz5PaY89l0UJM@Mhl})9+JO#Wk?{Hgk!;Jk(Ux@*yc%$ z^;k-tJO=V2T`=d=3VyHZv4w&O{nV7qO#z#5x_QZ!f6g-tj7143(Y*&7Cbeb?Lzys? zLHKaQco4)KxuZ|QTehG2C(${s166E@E zg_ENt28vwZP^&}%!xAM&-C=mLhaLr#VkUk|8pW1l8pT4l%3{t4(xc=DPB zRA73^S16#Q9+Rt@ z7A_l>lpa9_HuvXsl&l3d&nqkxpGejVuL0bD#Ahkt2P*BYbKi2EyGPUBV{ppaiVhsd zbs369!EmPC5vm8g`H}%Qmkff4r26KaRdQEwymKf_bN^v9u|yAa9y`cm+x`42x&0EUI!S8|Y#ea4l5%g6iq#?4jvMeD zTZ+$QaA@a*=LV9-Ij!U-tCKWT_$5e_YPc@fY|Vuf;%YM+Wl9an50duNv(!YS(v+}x zl6I8%M5b-hiHl^b-m~~pHCB6GWLP}C)6A4N+Bc1_#P=9*PkR1Gv$JyD1*%OhMkpSJ z4Y87tbn*;=&C6KT4JX(INX2QXHlKnkIkvXGpE^9Lihj-*K8iu?B^_F&I$-;}@~wEF zserZcyXHmbc71IDF*~d!&k;2qkje}*#YxIaY74qMTl1rwRj5!Qj-#|vU~KU*T@poZ zn>=Ee04OkTe(KOX>DTc2+l+n3F--}@ft5IWXGIRi4kTakO7u!gN0Kk12dfE(rFq6n3)kb zsc|z)Y_}}taKWHUV}cZ5Ynb{_tc9Xv!AF?NaJ%Vx_|(%}d!;eG-N>A-sI_PZoq`lH z!E1FN5s%@e1)%{s4NWWR#zJXLuhn8rC7mn+_;p&pzUhttty{MF%&1)08B@_O$!Tji zp(3WcgH8Qjv8iLROE6NzblUd?_H)6YL1F6EmN`zIO_lYkPcYfbB{cP((}J-3Vur$7^9)fK3a!P@askR=)O*j+_@2XZAOCFUsN zHqN&DEjgiR_xK1{jg(+GArX>zh>H3laqJZ-DH5l2S_HddI;@Q{tUj`7j#b|cNrHEe z7CxQ{%%1Vlg{WSbxTG=?34jvMG@9}Rhd++jlFvX}43`^c1oy(Ww!SzXT@ys81O{&kAI9S&NpI;K%-kz zn3&}(f+1DN$?n36&38GDn7(Nf9Q354vGPkx3*D=&=eGjeyB?cm1M>dv%MR^LZ?K03 z%Bw~`;eu0gqc&s4+?zQsxkfmjrx8os(#O5yM5T9G5L|e^5Fq+skVr`}uL>&-(32CE z;{Jrb;;x3VqK45D9SZG8qO*-JZdIjDAfne?fm~T`#txGxm=y@M9v*Iq!YoaD5_^8o zN)H_%h-ZloSwL@Qw3pFD7>}7B830}v_i&ro%ohdg%gEvk)3*zV>T-~GYAT!;P^`vz zR&MS??|KQ&NaCpWI9H1J$!@dmeEeeiu#U5f$H0=53q2uaNpXfyYqNC7!)MuJm3>ik zCqu?!$`^b+*$vuDYwY7ecBdp`usw@LGDnnoG!wAH_(wJP&KS3-M@p`smcSJL70`Ed=&8;V-yaY3AF0d~A8R>S!qeMF?h9=*O_;EKJ9Iric z7y~hNUNopWfa+0D$Pp|5Xo5uch?GrWx~-??Oi5QScyIdER5D|tfu61Jj9FhDaQf|9 zBePXuN?a;*WV84h=gSDOG3^6IbUeIi*jxYk=w7M64o}48RjfySQI`oJ3v%qJ#sXr} z^J@zam?2?Ca5%_K{eW)@!YG{JSUS-ov}0uPnPMhDX|ZGg{z^`wx}&8}^s5g5AT>== zEAAjhvK##@36Zd1ZenCOohSQTC=Ae3w{hDz*M%U{-YH3GUC2zN5ec^mHS5!AAHA@k z`I?9T1@sNTjJuQ>_xR29#gPfu-poNr(HHVvhy+AWIBVA4Ya$P>Ky7B*k4KFVO(~oo z`aZCUs9qb^A-RkTJ;WmpyVlA{<;f(Bin)>o)aWS<_mIUw8s~f92VB;vl4VL{m!-#F zia;2ymBqb9@in<94Hq7kW<94Ac(@^pb*%b~n+XzagyzdUi=zj$?&aR0xgk$jZR_7mv*?9^Yzrf0FAt>aT z#Lj4Xd}&>e$;s(aIKFWmfsu7x{*eZ>d#B`bfSonWufXWJPqyybCqY-g%(ZTO1`gE(<{W9}Z?4h5Z+R@U?+I%h7I6jHA92IUOHi{JRl9qAO~5jI zV>G=*iO|2zoRr4$!vfOedekbrL6(@jbSGm!k-fM_DkYx-y3Bc+a7h`rsJt0Z&8j`t ztY3$J7_GRWiknDNJ2$;Fopm$6a20NAnO85wXcAA5oLu5cq;8o`+@bfd(ylo9G{@|c zATh}b?HW96k0H@c7Yqnhi&OI@$E)a1iuLkV~}B=KR}bgh~j zdMxLYX$Em>7NZw}FOzA^?uy!y=v8j)@CF~eF>?=aqYUkL{;YK&$U`TcE-VL7Pk;2o z+&WOBZ^37I*v+Zk*S4WkBqnKRL890IbAHz}o)D8yG@ZvY04Wo=d3OY%*i(Va`lWVs zo7#DA1#dp8p|p?mY!H5*J6AJj#G{Nq#>`Vs9&`_9=4$7}hu`4jB~A_dAl^+$qZ@HBsFgOM5X`^pO)N&JFLu@FUZZb_%R4o13>wSji+ z&U6S{<_-0z?e}W(g=p*rqx0~+ZSU$|xJaOUD!)-OIWSqOs?(fn(K&OBsiTGu*Y4Py zmGYn}bk%-l;MzE8K8P3o!Rnp-Uc}2gf{+d8T5e~^Mq0}s=MG{Px((MMZ_K5f+C~oC z1WMu5YQ2G_C@$O~yh>}a$Gm25ic)HIx7NuGCneOt*%KSQ4x$;X7fN?5Z7do}|VRRB3mITU#@ zfyimjWIejjJ|&8dk6@0gu0zkDaVK`OoDiaQK-)UN3|9E#jC~+|#>`%d!2LcMQczod zOVeh}{~ckrZ?^pu+>rp`_H%kxSR2M(HCp7yV^7}(QZpOfWg_UM*Hc0ZhTW`kpB^55 zf*=fNXsjj^lhYqr)hg(&;?GL}nQjPJOewp$hCQNP@ZJ%dApjvqBR8%d5AD96Kg995 z_ny1?u1^G4!EV|x3C5E+1RYKX<`B#-sHff_T5?MG@Xv0@j6!R9S9h5B`o3-(AL@17 zp^@LANKbZpscpz?-#y};Z+c}#tvMq3SXePlMtR-(Leao?Rt+L1V9d zDWhra)%Hl@i=-}gl$sP7neWs2^npeHrqoiu&To% z5K0A`j{}C<6{Gz--_)gfRuATZJ=bZ+*NM&Ap>5Vyne`(Ot34+4?KKae4gXgf>z$`G z>0T!8afD~!_4A#V#}_%Th)iKPgMqy^2l9-KnK>c6<8ZlINPAh@GGHzyd`nB7?&aXP z*`PdzuSuYnSHAvI4L^m!V~G zJw)v;OyVa!dJWyA?*CjUt=AJ$!uR@tVw^88{_<)vXLCT~mDo6!I!x*EfCY>ah4GRf z-MGdSXd}OxJBaC$EBpoY<^mf1X^ml&o$>Zy8VPe@5msY+1YlkmE`GtbV;q-w?JK7k z*I*$#Ua3jm7wnx{2QE_o{L3Y1)llG(tmJ9^Pvrve00$5STV&xI())lU7&^WBHrTY- zt|vcKA~*B__&0_o($mh_Ks6CFdj)bBK~q%<&j<}iCrI@lh9>fzsSVW%6fW-!otQ?^ z1F>EvZ;uHajI7Hi6di+w&^d%jr`1(e&Nd;Yq!BS~%cA6m*mlI89L3vY>r@cYg&tRsV$~lY2w?+k$edvu_2ETb$(N zO_1qC`=g!dx=qsU`Yr`#QRWWCb-eYBp?>mk&=BNDkG}C56%-6o5Z+s*jkFKf2NvmB z7@kdclXlT>#BH!pDCVwU9(yU_^zOb~qyjjA(Vw$}-iLQ~arvr$aDP=0x?5`dgZNgC zYupj(G*@u2pGL0~!PE9WcF-Clr9T<~Cg#wQ)&nJ-b&cTg z%YYfTjlZGVDu%ipQ{D1W;nW;RYCs$f%b1_~`U@9hzJ3Zw6OIng>2;T;+`Q9KNl5d!&8=w8yF{nahwIM$3(JA?I3x`HKU#8wRQK!9#7_MU~)-TrytxiQkuV1 zMVYTrcAVCtCt@_EIO*QcAB398CFDc7s4KAH7;kY?z%xmnA(ojq^e*b?6C@*!Umi(q zull4=fmlIQoaRs-vcX=H6fe_@@Q^m`vhB$-RD|N>mN?j94;S#>nA5Y-lLmaJt5o`o zju@e`;!QkrEY=l@grJ=d!VriQA&Lm$$$eW%HfgDrGCKR7k4VBMBVmkq z&9S&=(+$_N3$|Y3gtM?`GWUdX0@S|pUFXm+VaA9fN8;1fa{&B={j>dZa9vwFJl2F) z2RGUT(km;1!w6+Fr*iZ6MJnu~N`XNoi@7P4eEpV11KH~d(X3SD$Xma7QJ5k*uhx; z1WvB?w{h(l{MB$>O@noUSWUWfEmPP08{4{)JX`IWix9$x7d{1Y%}m4)fLvIeax$AG zVMyKUv0Wofum|{*f%C$AQuO1dNTmxUwTTbM0?liqKq3>!(3hVAT#_2HalZvp^OM8O zP-14YyTow~(LN%Q9NKdT1!pyK3x5aWV56!g4GRRTcWdtL3U1?r*(bGY~u&9t_;0FA~ z39)as)FCr4U2J*83YwkE_EdDy)fC=I3-B3hw(2SdH0!hEoj912)-*s|`IDBD&H>Q@ zE_rSsm15qx@*^%er4K$V!vhvHkL%>}{xwc_j|RJI5+(o4P>KlD(staIGWN75{1o+G?rE+O|~34;D5(1k)@&`f$DtC&T^ zoRZk|7zo)UZ>Xa~Yn3w>7DXXdQx?U3c^kz&+f8zMlctB&A@;Gg$BO8pD{#)m(k}{` zY$7Xp>4uF1_8B<|>z~h8&$JR7>sy#t+;#7C-41aTv9p8Vlry~_B2VTzieVQ5x%5+5nkTZI)O_-);g9qvEel|pkoT?_qL_-fVp^p0xjHiy6PxYk z2IgLlxh}6JP6TB8jm#Yjb7`*1!+IZQnM3oLA)J<{C9{~1f^Z8?{tj3 zcRB{e-y5HUp^F2Ry_4~r;)oRnGomI1&#O;*1w<6$2JD=f4vCWm?gt1&nr8al3k7(( zbT~rQnx`91Lg5lau)~5>DhB$ym^;UDJjdIuqltvmlpCWZ=NU0O+314OryxV-Or;UY z3BIc6q2nIv^NsYEPSl50hcUN??+XZauag_hy9Jgw%qyq(baq`JL>+5h#EH_SUTs7j z4UzUvGjJK{jrr65EL6ZvE*cH60?z{A`ebiL%-&03WrLTxYM#lQ%qQ%qCiN>4rF~8< zDPISkUNX)Z&lj$$;rYJ8aabgC)Q_C4+%XNK?yywqoz%gX7KU;p-aR=J)TlE(g>g}S z;;kF(2(@UKu%s_fv2xmAI`vZWPOW?zIMbbGbidR)m}q)7s7;w$Y*%AWHEJ_2VOZpV zy?k1rX3jjzsd64-b=!mPds@Z-^n$R zFGnU87FC8Sr<99{@Q_SLIzs5&oq@_ugvi`A?`wu#R$xM@cHC4GN>1fl2;bJr@8~ad zpIeQXZA^$M~NxPi3zTZSmw9YK8ukntlFHDCf*$I>pQwC{OFK{q?$cweE z%Pt(9{(kpPuHdX(A5;=;G*{B#2l$Jg4RF9G$j>UXa4zSt(LWk6A>|vr-w3Rvd25InxF%# z9+Ea;9=8L%1#R@6s zq|FWb$hXS9FQX|KEY!Q2Uq7%X*D*>9j0wFfrH?rrAp~aX4NLh1T7D&dw4QTQdH-zu zl2K!S*bc7i@=DD$qs_MN4fNkh;yo|?iv~jcS4qP1pKPuFL#B0WSp1aqZk_Z20D$#x zqx^!Svaz$aF|>0qHMI8}AA{(of$F?yk)A?1GF5{^1ih-l;zuzyY#y;z6X}e&J!DJ{39qfe2Nt^chS^J*l>Qi?+(TfbcUt9RW+(3vRdVm>*8L^i zHCuwnWAM_~U+k?eZ7rWyOalD71rWs15$bC>pa#-eS+ut@ne~t*fmy59M(-xL z)x-Fd7qT0M1#eYgo9BDS1+sf7#0-+~vf(&`!)6e8of!nA^wF_qnU<1m6WxGxLNJf0 zeGM?`EfAl;LK=@1xF^5Kd157DNO^CqLP6#+AYx`-)R6T6+vj=VaVj00AHT7KoVj^^ zlHMh~e|*WycjH93U4ci?x*8gTu!{5Y_VfPqomep;lwU;easC%FHa(NBW=AM(?v>;3YdM*pO{|C#Miy89pU)As!R z^1p5WL4N-;fS=^|KjbF>{`<229{~TR!v7BSI~D#HSe=jdUqSr^G5&W?ztcLJ`#bXQt?9p!fl2??P=7V5 zzXSgS`ZWhX%h@0Da}LPfTl7Eu5PzlT|FctnFK)k3>wK*LHs&wz|49755B>L|@^3d< hlz(^Vzuf$HiIEWpdEX!a0C4XQbr1l6PO6{p{vY0dv$p^M literal 0 HcmV?d00001 diff --git a/embedded-plugins/ss-editor-tree-generator/ss-editor-tree-generator-1.0.2.jar b/embedded-plugins/ss-editor-tree-generator/ss-editor-tree-generator-1.0.3.jar similarity index 61% rename from embedded-plugins/ss-editor-tree-generator/ss-editor-tree-generator-1.0.2.jar rename to embedded-plugins/ss-editor-tree-generator/ss-editor-tree-generator-1.0.3.jar index 7c261a9ed80e61bbc7895487e6fd2dc2ea66466f..be7ad34e468bdedab88b8997cfb53ca26b02859c 100644 GIT binary patch delta 28285 zcmY(pV{m56_x>H*wr$&<*tTu!j_o_PZQHhOO*EN1nPifQ<~iql|MgV;_KUr)THRgM z`^EnBwN`a!9CS+xG_tA!BoqP|7#tkfTM0)CGATIZKM}|Z9{68u1eg6cw!MPOLI0PK zgq8X)5eLfv_n+uO&4T(*s9~|dqW?2jK|Zg;uL}kY%pL;_Ob)09%K${>fy>Hd2gh&Q zBOHPGZ*mbu9mIb^iJAcx{@+zQE@~`x-0gr)rk5A(r*kS5+F0cl#f9R=7-ley5Mw=xB9z)+~tU@ z-4SzaLwW<`DEmT0{*>DsF=kLH?YCr>d8H64vYf+Z7T4P>6lJ-UfbW0z7+US?pat_g z;l#U&t>Ku*3+6Y{nqeoiO8?Vl1#yPSL)uOTa?*Imm8GLli1H5_4KbsbMPQRInyk0e^{JGoDKj1Mb@8byb~_B@5H z&rRP;N^tc6c{Su`rJRr7VdOeRgZsNGM_6U9)&;T?_2vRwZlcoJ*JP_IOp)#nw%BavwsgXB-feG8q6;?Z{R zQ(AmNkhQnxEQxG<6Gb=S_@-gQ;dTOY&LbXZx2fUwuEdym!50!dSi13< zqMtGN8&)26mUZYCMb@M(=v>nz4R|z2SaXR8euE!DOIc7}s4 zQXEJ;rTLK_qs9hLM(*;B^SCXQ>6(S29`v`ATjJJ`LD(xy^#<>RHwLRR8Bj7Hz7%N7 z=b3O7=*veG$oEOvsPY-Q9q)MqH84a@GW<*DMl9`0$@b|ZBJF^N0C?UfIS3^T%$C+i zH*u8ch*?tmh@cRW?W2kH6_q|J8H2L>i+8{Jg=U3M;}REBN$(z0nk!hPFIY9>qe5IX zFi{UWrTA^I4g2wr$Bdrg7hZEd zI|&{2!A%lnvvnLUUH%XG6dXG#zSyC_zyOF~U^4$wtIeNX|9@oL;zovo`fxCtL`M1S#rF@1uZfJp6$<%Lyq-H)@6LdLUX`N3V!+YIC&DmS;nZ^UA372<@;GCm z%u`-=e}i=pRTNw%7t_>oczsj`p1H3-{E<08Yb-bFC!PdO6Jk_dPf+l*7tnw^Xw3tu zF4GK;g@fX{3vWp|CJ+drJzJ4P}4ms6W*22eqOnMRDKuY9xEz%pYT z8Uh|YPgwGDR%^{lF3GgxY#Zov%{Ns&R(ml9L{HzfGxHK>b=KtL>D7n%Fw71 zo$2A01sdO2_0k1Eb=>`6aZ}~DR4pR}D;>V*w3Pf4fBYHqtit6gr%ZIF}^_sE1A z9!r)WTuCjdt8A+8cOeX`avp>+l#GbR%3y!gL)g85(A=e;(Vkf|i&Z$&3y%|ii$&Sp z*!ESKoO+#F6pWMyPtN}RkG}*&Rmp6{?0EiVRo=g>0*sMcDtCFMEQ`Rf?7*B6v#V6; zne4FqKp)WBd2a+g+M5}wu=6mcWk{t?6FlS1F;HehvOTeFcq2o~W+*ojPEFA}d%o)0CYX%h+ zrZ72T0n!yI*?{F{>?ux1PNBEricRbk4|@ryg1$~cT<^C6HJ*8yh33u3+sE8uc2aum zww;2ioxsj+T?Q8w1uc4dm4=I&Ue3! zlzieAj5u@T2A4W_?t=w~_LN%=-l`~P2>V=@0r+A4`akfLiek=_@yd>%f}Vh(PWA)RPqGk5n3OAuM(E|^W;@X z0F>zh60h78e`+Xgn^rh+2@r_M1#n3aU`Z7};~ap0$$kqJ2uxNCp`jk{C}gBS`C}3Y z50Tk)!A(M4I|Et7m#C~$w1;tXOZ10rW`YgDiz*B-q56~ST{Jkvdqv{}Jw>IAqvY}^ zitSp7sOl~kW;Pe-R}ZwV+jz5AiXFPE04E|ObOb|=*cQ92Fxb;o)+q%X>j9(7Grt%i zNYt5_QwZ^S(&W3eXe--a;C6#DOfe11GfbttAO#_d%XkPJCh{k?f^Du$8Ae=hP@w=0 zkqLAc2?)3@1g|HJuOXE|OTl}{TooZplali_oWDf>FR0$f0}%|<{~?vpKctfQmsDBJ zT;PO2!GCO%Tq{)sEluF!OZMDvQ zozB`oB%RKi%DOGzO`%od0ZS0}TD#%G2J@O(_Pn0AmhiFj%%S_d%N{?PrG*BBY$ z)~*v4K*+HJQ~A2`o`rKiXC|W|_vVUQmiMW((%yTfwa2=eKX^QGG&r{}*V)mYr{~Un z?^`2a>0TA8PCJs~YU!G0Y0H7y8JBw$on6mrVSB~VX!^@~Ij9u#sulL4y1%q9v!R#g zyM0F9{$_6JH}8#nEq-vORqsM?dX`AS{z;{*lS}CV>#I(A*#0|fMkL1qv)Y2PN_RxH z&-Y%ldi~0|qF%!Ndd_p3-O=h>`y4FCIgkVZZ4sdg$T9G)a$RGF)~cvpa_;&rx16>{ z%uC;W$MXq^M4V?0cg@l?6URpyX|~Rnhg%yVxsaKD=u2Dy(HuEbY^HT}9-=eor9-7L7tV(>2|pBgXFDVw%U%xI5Uq-2_*)Q0H(@}e8R zkYCs(g1BOL880_S*nw(8;K$yY(z|xLY*$0C(WOIgb^e?UN;ve1HPYCt`iM5to-b__ zxLZ%siv`^&V^m~3`BZawG}0O|w>T1@&sRzdZOkHYq1jhNG*_`bw-Wx+9l=g|mLkjr zE0A!^XWK&VGwaW^>(7R@@#U>u1}+ricXL|+F4ZAipWS+0Z_fy^Y@K^9!P!KEqV-R4 znxY+jdYsPR9M|=_kCC3%tU7WUziWs%6ZwzyQ=$1sY9uJi+pd=y?9};>G@9lC9`3Hl zJPBU`o?a^!E0jB&;68$+Z!Viac-z5MsGzMSV%YP1?V5J(=AXNF77Twx-eAD-AWC7c z1mx7S&lTEHR)z=@oFlQy56lvZ5zDBszoD$|;;JYTg--y)k9j?X%L>2_vER#K4XF*z zt|qt>Qn|=q-dyo@!2DiF?AZJOyvhRLv3THwp|Jeq;~_s2L23F})N3@-( zUI|FUh?40zqYWedn7Gaw#<;BbHAiB&_J@k*8VcyM=E)*P`~CV(>)SFGKrfs6_KXUV zk$j#T9*y>U9xd)wc~wQ)rqVZ9ik$_FWh#!~keoROobhfjnt)GtWQ(*#D&$*gp@%zptzjd~hmY z^EXsf$p1T~4J4Ti{a+y|NQ>R42Mq>RiUI~E^$+N%fvzeDS<5`&Fl`gxy#A|4+k{mz z|0gyzoBtTcSb_dRd?0*;CBW9G&54KagC7E`X>jN$F1txNhy9zRAA01FWb^ZuCOGy^pv+Y-PGW~^uVERV<)7`3FpsB zlbQERpPzCpwFI~3q()@NRa*_)k)JW2=K;uPnzA&I&)Lg`3_~d;P4#vZ=iC0&mQ>3b zp1)GI$U>hM)MhBo>G2fVbLss4BQ(7ybeM_RMQ}5vX1lLGe7dQVv}%A(>2QD~*D zKpxT^SHWPt*-lT0+s8skuJ?K%!-V}jv7Y2n9=@?nXXA`}sid}*(yE0%MfHr3mo#9C zWE0yzo8!)s?B0@DaHe+A=Z~UX5-fLxf{r;uUl)2Bkvf}Z4KTy0_N%hLgR$}&o?e%Z zy{mV^WW%DR+JTrGCk@k0ZwtGF@?JmM;5E~yz*|W@96e_vhqk?cFK$wzewN53T&yg* zc*4B*I7yYfd3fw9-BVAaoQ`pw0|C&$CsU?g)`~?^L69V0U!)*Y#!z6?!WXdPRf@nE z#XMYH-`2?T*j}z$Eo8JHvF5?20(xCfG$c3DuUFW7a*Fk(+RVGs;@TNoIal>@yGV9y zyu*51aBf#f)^r{}tI(}dP|wR-r*~4aRfmJ+WAX8vweQH#>a^0bPo5=5l?13!I`%%z ziZc_U(=XmcU>KOk)#@l;!e@l3NoPP=pl58L2&cusQVZ1>a`#W%`+jrqDkvF6qw=Tf zJV#(=_3*;(6!@b=b=n(2o4Ff0LAWnctiUAvR?TSSxcFq=cW>pN(QVw*&nQzal-FoI z(Vx8Ex^d5qcdI>+%Q{GPMh{pHhpoJY!o|VdL$jZ@z&#et!8522n7e8%5H>&36&?CIHp=XO~RACg$ z@WulX4{bBf=6AmJ0Y69b(tdlF1=E;UplrlJvfcPXVD13qo`=bDz5}S}bzV49uvJ!c zL=SJ9`VU*8imA=+cB=5T}5ZL)|f7;_jS8602ygI%k))qKL%&#(m91hbAq5wUm&DWDM5W5gcT?{$rR#6AXYr;jehgzO-MO7`k<=aXBEJI}1 zg>a7NX6hTF)}pMFHa4=!>e_WW>F*vb^&QZZ(v(@U3%{u0u{Jz+;0`LTzn1+Hf%o6y z83v;W{qU;G==g!$_)1va0tMBa>&Dj;buQ~ZJbi&5%?w$7+<=HAQWn{Y73pg(B2EW< zdhHv8r(7swv+72oL;~(a;^1vj0`7bcUD+lHuT(LuD2{6)Nm0WeJSLGFm>e3FV{iV* z_P>m{?M#cg?6!K5HFubhuDlMBIg?e6o-+_D81ojh^=w=M*#`QyW?K&rLRRx@)2va> z>+gttZWVT!FIOo6n_Qocf4$`UR6D3wWkuM7%)B%0$LHu?q&Koz_HJ zg1!xdec}lc%U_c+KeS&0V(|EPNVd~D9WM_q@9B98TV%`>oqb+PD+@ef&OT-05<$fG zB!~7QI#2gfO+9a(*f3>NrijhUuSRrTnpq9tz$5v6cEI*GYuc9AgN(d^^II7honOND z2kQ+9+8;}Gr1#I&?R&E=;6l1RXEI2Ne$GEVbYxk=-w!q(iDB3L@<(9zH%<|01PQ%Q z%-&2dv4`>qBJ z;VLJa{noJE>bry7zfj=`uZYP%5*9_v-D0+ebX+VL1h|Na3K5eP5@#GGZavH061Il)T)z<$>mnvM zB+dW?C2kGN-O9FxtX$4$1iT1|c@dKf5@#XH-O{%E3|!8b1YL-Uf)SGi5@#$WZe7dW zvbOt-Tqp>M?-7%q5@+Bg55>#Aa<)QDTqNiO$q0#45tC;UXDQ3R61GD0T!k0}Du{_= z5@#{PWC_WmQ+mM=rt{vHt)hb?0_xubP$mIHku9>OjtAeChP%sD!{zvsWa}?{IZBJp z=wqtf^F#Pj-)l7Q${uK96%iD#bU_(ZqpHtzY6e0dg-r$k8}=!E=>lKD$5@NVE! z*5i>xxa6I#j?#wnEy!;0Qx!3Dd@yKP9)_XaX=e=M8$?@VjThZ0Zo*S*Xe!^k!QB9q zQ=DkLZu<$oG_vq51r})R<7zTU9(Q61u9|3Fu0n|Hvn`lv7kH9)r@lO;lk7>=9(OW# zC)@Ge8D~7>FK&UNr`}1VVjpBvP^Zy+1TjHG@9406xiNx#H0ZGW`Z0q1h2hz!d=a)D zij>xT!FcR*>^|8|!#3MPO>H)!A@BfIL@zdM8c9mUP#zfvGXgm_;!-w9{~iDP@EkU% zw(YpWg~9}a?UyH_-wo_p1!jqkvJ2rBY^25!G``R{!w$^0#$ zt6&Oph)mRVruaKh;HAUvrDF(bAsLpm^%Q=(O20=4T1hw4Ae)njBA(CD@?{hYBiCjS zn?w7N2xsbCVf-L@QoBq!{a_cL5}{{!!FPi=$*&xuQe60_2a(Z!N0yBF@JI#jiJSSn zF*&pN-v9kSt`&%oF7cn$qgl2k=z;?S>%{^Cll<4}WswMh(*P=TV*!{`*k7AC#(Me| zQ#>dLFRA7hqnTnXgm5BI-Koqv+VdNkjTO@Msoon086$QIKP#_;cx8XKSNMdq^Muko zl{@7gP65_dU9=ta(wuV6UfR69tq+Bd`d>Di{{Fmi0Q-F`O718rFCzd&Tt$#Na!rPW z4-^+dAH}zntOiK3AYp1#KKf=M3Bt*;^^jw+QJ*=&G0VkH@jql|K4?-BfbP)_!(XD7 zlYX5(45A$-qfWn?!jXnI>kG#Q`Tm#MDYP1RdKtKGJ z#y3erBS}a?rizjQNdW<9>$s z0$UUN9dxamBDiD2t_k>qrt>+7EGX(;aw;$E&9_pUsjYYLz0`eZ9T(V$wepTMWtn`+ zQx?=GqwMi#t^+Ibk8LSL{N39{$xFC-YLx8kwK+L&vKLIKHuv<{B^~Vm91NU)@(bU3l#kkgTh~1LN);@9uIjooy1~JB|tTwA!4U z^nYNgJVvcuAl+tB{;c+lc=%O--!#+HH9Hm9nF3%})S4r-Kjbwux)T2xZ0|_K$x(TU zJQxyO`c+(KZxH!Cb>D+&OiO8aL zqYp@8fYiuckTlegY>h~15R9z1A#gHL#C$%db%bAagLmRz*znudv(aqp$X|r#zNmVH<6%?c&LUsAX-CZPJn%5G^0 z%V9aObnxn>$2ZRL_+8l=j02yj2Q65EyJUT!&Kq3C#|iB+$pRm64+Y{dRt0GVDot)- z2SOg5hX&BQ92rql;2)`jocigQ-jfo3GQ5(oOur6>k(yRTL^~ifp%FaG$q1Xo+yG_{ zP!5QP$&%$pP#-AGFIhlU$5Y!C$FvlI$p`FDSRYudGu_q47%YfcCfXp*kNjgbws1Z8 zIz|_5p{BftK=wBstp2b%gvJwGsoXG~NP4GV{1I@iiwKqszQ|j+x_*R_8sfCQSRWXI z*dGW(*4j5+Oj~YF%%b&uRSoTH`+$(6_T#Pv@sCDmUaSuifGS9~pXFqQy5xeNt7==2 zn^LRNM8a|gYFxdW?M$xwZ>BTLuMP7Y7Zz_rlDI5qro1ryiqqoE(wepY;L6}lgVg4_ zHl|cmOYW&_qt0LvO?>Jgmc6AT(NOD?w|!wOX^pUb(&VYqpu2dI*n?EK8^EB=p+1R& z_SRLq5VZjCl|D0e@$4dUri#p9uy^yIb~4hc@lrle!Vtg7wV?r9x{{Q+AhWsF1KoPF z_&ztQJ)0)VYifll7c|kk4^wX>52L*76=(z%}W!V)AE7bc7KwcW1d%YBY$)WyQcmk1<(_VM#Omj>n@3? zz|_zPB~e}Cw=NHBXmeasfp*pQR|fSNHHe2~veQATTsMQG>A{= z0IEP+c>V5uUMQX2?KeX7krG5i+UT*rs0W!% z!GC|u3Ri=CcqbwhQcxXzvogm`H^C6_O@#DZ@q*6Huq6m*6+oTr*QEkgKSm;wK>Q8bH@SEz^H#Qj16V8@`@ng#kU%iD)n4=oIxc1 z0~xD1^fWRosT9pb9CGuR{YllXsj0uaHOu8)2=XQ1FcuIbgpwYxZJZD73Kq0Zskndr zj@z98nqYYG2h8pxzV2oL_f671LRfnSJ8JYnp&TR6U3A%>EEb9*t4fb_hJD=w0aHxc z-d(etwub;@<|%786&2G14>(x}PTPTyV!XY^k^(go?(ihN{k=AdR*Cw?!7w?Mv7|R0 zw(q)u)ACS*^bQ`1qhVm`G4eiRlhRMjX;X|_^eMIcm#L}=KrD#K`1nJp@ZnOL?M{}n2o}F% z%ytwl6a*DYG-jQE_UdA_k0JC(6)vLuO_okUj<_nK%ol#DpN}5e zxYTe45I{I;fcH+*FNe%-|8w{kUS%ejT~XV3$(eiB zR~-g;jNU?ZR-ItMku=8#&XNyDdV0nN;9ZFVt+rG9BV2GBMWoaoU4l!D?kRM=5{sDX zt8S@{t3+!;Eq?pTz4?eY4upAFTzw`1HR3dDoe=O~h~ybhUe2Am%pr9?UNGFlikcjH z&RF2cuF5YA<4APG7dn1V_2M+lg$2QX96+{244oFe61J^Y1!V)GxQZsegZ})5{6D5A zOOzU%B`aA793EI)fb?Gjt*Kz{f44S*iYsyc%>Ze-#Qz(y??Qt7&moDmyHlu9fPpm{ z{jX<7mh~Y5P7YwPa5VGq*a(0R)ZBOm@%v=*=JY#awkmFui)qV1gJVizkfAB+rh&n@ z=lo3*YzI@>mjnH@eJO@)m{w<5a@--cB^>7-JEdN&=XSd0-2%h9j7Ch2t-M--phB zj|w|#KZ*379(HlN=)&%wM$z+S-!dF6W3}STa=UkL>hjA z1~>)_<#m5n+JCl&@kIO{JUs@moHBUeQFiV+Ble}G^>K6Or4{WQQ|^y`KbV{G7Q7#R z$J(zs{seeeQw3HNo$QB%p>B^v;xfJ#6DMsNc)PXc0i*f+_#jC~indE2UR<*V@Ih!I zvtG$N4{m{BwA)iNzS=z9?50(w#52?P!SE+@ON z0{2jEebs)tUYK-)8i^ODhWkVVgb9`XNyrTuHk^);azlpacY zMr-)t1M>y@cdkc;|4+gQNpU`ybTRQLR~DN0_H;c8B9K@hLJuA1OP^njuVqI%s@Wqg z2Ou76kE=(K2P9&-bWgJp2|ZUrN16P6ij>MKqo$1I+L!{QP>$G$t6#I+cbEWq7Mm z*Akmzu#os-u}csw%&_OrlhMTGLD^R(!qD`2fu$5g=CmmM`hmb5*k!@$?Z?^U~G&Z>X;ec{1j@y98)^i{*z$w@?-0G~ZXlaBuQ`E32-s&jP0py+B zctv*L(UySp10^abV^$tfG;{108nIh^*gDdG-5E zlEtTgrKA4nyT=+B1cwN=++OCOrC6t{>RKkW5P^g^QHg5R<(8qLk{AstuMXM#ni^|B zk>C}Lgspg(KBIV~1k1u1GHe)2Az*w%i7nijWwNhV8w!%JeGwF=iyW#Rba?-KA{}fT z5lz>FGa|5c`x5n?e4dq%q+M-KrX#*A1KGH!Q*LuBvq^@HHDzwW?(wa|>T`ftzd6xH z!j%hJ;9zjW`4kK&-^3NY6<{a*3pGeTXciyQAXZmVHb4gnHR3GU7GH{T05Anns=u7b zMh|Vs%DnPjKYclRGQCn>GU4VMg_(^y5Y4}9_3?K0RQ=@s)+5rFy|H&tq^H)p>*$~X z+5z~kTDP=EKk_ZCt|VfRsgE)wCgnOQT$~QyA0Yy%$IvKFL4yuGOM7eeQrhV@JaWcK;D}y6#mIB0@NQ4 zchf8|tf@d;)SMD!THLibMBSNPpOVRzJ9{v5aW*%bEJyAnqoTAL?s**-F`^5X_I zAgHl17*HkU03NF%swPqDa?2$TXA%7)K{dPH$FdaZAdi24xW{f`10aMGt?klvM0}5m z5wkO9E`Wt(EJlOg!fZ_xkA#83lWd>PmpBk40%fDO8|;8zK7&gZ;GMpR=;AUx$8qNl zD+n7*_KhCQ8@sw#(I~{=g?j_Kf8yBOV`tqAo1-W&^CF>nGLt z;o5V~@eI1pQP)jvbw@`Uq*pHi63qIz5?*1wDKI|2VZ(Dp0e2MD1Bc2w1s|%iiS~l{ zVmO#wW`Wkxg5@24?@#L9n!LGOFES`O463kH;IZtrfpmlyCvWWKF*I(qaM*(c^mpE%T;>5 zg-qstHY#m>0F_-K6rP8?c2ZsOnDe*!EdggadQjrITf*^{4#W|Xp4;)=lfYMAZT+*uMh`ByeW!1MFHpDw66 z(An13@^V{J*m%g~mmG6GcfD%IjWB+h<|k+Lj1fkaMnwDOcK&T{PR?;P(9uQznG6;m zS-n*94lp7{)-Sd8LRV?bYG#;E1;zAQph(VhP-*;PjRI}4SWy4xQoDz;-I0CJ;JJyP zN_4Y$@9ala@TGAqsD5mXU%EK#C*5nIXNcG1(EZ!fU%?&lxC}awLK$>X7xO!W-sInw zuZaW;Na$-uh(YEU+911ebE<-}5)f|KZjKR52B4xRPq@BZ3ZG;9klq~Pw8Dgdg?KP5 zaHgAZua+4RDu8}g_UHG>GFD5vRK<_KS}|3aq@bBEm%Zc(PKP-8lZ? zxd0)0ulI905zUP6Pl1^=F*p$`B0etBQ$_-Kp4W;ILS@|c8uB)vZ>h%Lf4=;YPZsC% ze&sxGud42U5^qjU8oMleSuLY;E!?~2WJzF=Kk+JR8rr+{>3Q#OA@E@n8au1~<>(3Q z_SatTs(*;u>WX?7J`{6U%EU+XI#gI(5(bny5Q_Y4>4JF+FCf@bClYN>P_j9#28G#b zN$2TS#B9{+QYuSiyEf;MUxT65}oqAeYLMb$w@jT}8H?qkx1V@wK)aIYT)A_9J1 z&`D@WXI*KJ`)m15z+e}K+Mp*1-sK^LZI7V^rC(npm|%m4F}{uC*TQkslFJ{nf*QlN z>L{4rv|7|0)un(8Jn_u6+p{w~%7ddbIqAnRcWNyNzLKSJZR0y|MRm_&bi^~MjSPbf zi`otZfy zyA}2uVz6spTe@YjUpQWWn8VH(5dKW>Y3d56BcKsEq+O)!QzNDNG%i|i1`?)gktZfA zX}O7>p`$}ucBB54N>p@j9n+YAOxO=M&tmY7k4BqBxiyZRJ~fLd#R^KCQ%HsS>9N+l z>6!xj91uh@XnRH}E>R_eoeYR6a+R1J%Bq&^575L;W)Ce=odBNFYY8zIs*ShR`Wp8a z$9;e)y1hy z*qnP?7-_i;L&d>s0Rh8@L=%=2h&CK`AR?hA^AM^nOLjdipv2~uC8t$2jUcrzOx{KE z36Q9c9tc}qORwOls<=~2FDq0(I#ey#59vZ*X}FP2Yb;5JFF>cJgrYHMPEe7Hpcsk8 z!vy#AJ8h}*GE!e>dgBh+t5^GvuPFzfaI{+N+SJJ#IN^8IDcKt|H3Z|+UjFD)bQIP+5mgM;R^eNv z85?9-eqIaP#fGwS7mjjphMK^YmgKGut3`r{cu|)mui~5_pR=yCUTNg%RuLPyA~%G( zo!+wuRw-&N1t12?46|VWa}bm!w%nHyM&8vl;hR4~?hi?6i1BkGkKj~Mq9|;<$CHso z$Id6SQsaqltYmq6EA7vIRkD% zyX{DA=LK^W_ZD-VL4P&1w-G6A%@@g3hi%*D4lC>IGvI~aTcc&hF50LP0}7MhFk%kn z%u4$avxd@-4Jfvl89a34`6=R3kKJMpA{c z7Bls~{s78dVg6vTFZo&mZ3|}0{Ds4KDWRD@>@S4D|t*xBM;E~hexEyorm zbJGs9&NYb{JWX|EJz#oGyJ|Atp!ldL#*8JJkDkr~ih0U9gyd3tD-p<2H>)s;ho_Z)T3~F}V*V z6_Vtnq@#7FUIJ=+qwz}3+#mZwceQ8f1OSH%y&yh%(>%U4V}y8_?8Gax$ub^|&TJ z*f*8rW7jrf+S@XJmDo}DUi)n1yQ{&@ZaN<;fd)s}0U#)d zV5~-YE{xp!wS)e14XUQ*)9EC{I8rs$y0#rLV`<=NOFGnef@^WxY})y}1Dz#!sM3P^*^k)BJLr4W?!}*WD+1goTwr#lU|RJN&xm!n70;sI`D3K*pZh6_?IwXeh&nz80L`=V@LG$ zn_A7(CmpiE>&TbG`1gVQH z=uVd}%u`ev3@4!qe^iktZG%6|Z=c_g*BuA$7i4gFWtU#c3D6H_BC_|1$pJw}vZ=tx zt638&gGdAHwbyY;)}TLc)H*=`oh%%tje~BzzuVeS62=9hK=oK2+w}en@^~tTa&p0| zm`Nb@+``)$F=33H@qGB0&jT@`2 zSwn^bwJuRsy|=Av?lbCS9}t&Dn<}fy9DjQoGw~yL33odki-_vwL`QF+-K}aMc!$gr zGk|??@?`VLY{6kxPNqNmO6U87?=vNa(Mw`@&C{1qiGtIvWIh!hL&@ZcDy{>~PZnCT zdj3?gP!KujI>^9hQ8@0(W#rQ}S#2l5~Eks^kqlmI7l)mvo8bygBT zH%#?3%ps6+WmJpLedaYWk@%FHEaF z2xjNxtp(AJRu=v3?auy400eM8Yx`%vOhw&RR|*GN0FDTR5$-(rRpqRQ)(dog)- zC3PjBl7`kkj7v>7HsDah4YR+<7cLuX0b@z_crW4CJh@{+9AS$NwV=Xp4|O8iCV)k; ztOxf@{GqkGfRS@2!8&~#ud%wKmDEP1SaH=5>Pe4O1{c^>R*J%YaIy+w#*Oe=eNc7i zDj11&naBmmVh*;8#q~X~!FW|@LQ`4BP3l%0k2g>>8?Mik7XYSP65$e0uQTd1@%T_hsw1NKWilPFLlfBEdXoWu|#5GcrmG_r;BL@e9DIvmV4qEh3DoJ|kw zDj|IVW-1Le4r#gzd3pIyrz18bLqs0R5xbAP?7)mPWd2>eVH^I&dD@;dKSR{8Gx}nl&_YM~e%-Y`J5GmrP_Ii2_$1ivsDH4- z{B;T!2T0B&A0zTv-1}*o+iu3qzL(H>Eh2tP8_Hm-;^wL1wp!dm5%_L$Y$W+3iMu-U zo4Y<0cjN0J)}v9O*x)?WBfGu}AqW|n6gsN=6Z?>JTOCm%Y2G~S9>PZ7-&ouDBx7OC%#s5Hr&T9~~R|=-{T*%(frg-8?Rq+HI9oWSqh0{Qf{~J~(Hv}Y zE4$zlPO-WLWMiJVVV5{uP7o;yba4EjZ;V1rBk6%R_S8p+qhPD6^ySrT*Wruz8NCC~);ROL%%gD4=Zt^jH}DZd z?pl{Iy?+wSHVaY*6U((X=b5h@>O}ab+g`)xk>8^n&3UAE=ZTZZz(7Yw6msSg*|Tr! zW3A4~D^M`8GU?Yf>gzo3TI=pe;w5J*GLl0Y`e$p{QfA;Ja`?!4RG$OLI|>kQcz#K=#o6VSLVhPT z=diWkl&#Yqx6t0;F@#oB9?%p&#ZNFwpj3gHC?9&biXN(SH3NJ_= znb98^0%i<_B$H08IBuKL_=7MHB>>omBQ+qF%&~xOFp*)HS2V$6$~bV*NyHPaeyp$f ztzq!~I0f?iSxvTzeGX#QF#}VvA&Ft4lCB&-?0yVlGh945sqmHJw406B-f}$bwypsA4o;!j5_X22AA+E)+0FKCWCkG< z@_hczSj$sw$T0pAVL&X%)`Zv}m-Bx2UlWw`i~qC~NNL1&G4`j$@#Oww=>8 zNhpnsm#?pOT_Ir9ufglOdX{3rVL-f06+_dY%e9nAn1sldvsIyl>5h4Ai^=~LlM?R= z!R}AdZbn}}M!y`pRu1mG;>J6^08xcMxQ60TAqH>$!a%P|V}9sPuWF5PAw#c)|Dl>#=;Pz4|@(c z*3^HoL+2Zq)c(;fJv;y_K(Tz?eh;Ja?z~*>lT{`Naa(|M?ca~SecX;Lw)A=Iq3;vs z0{bL0>|zOt4!8p9neX6p0KXlF=b*z)Tnl8Vjd-PmRt@6`Vb6j*)8u*a3sKUhl|d!2 zouxE(-O93pGGk8c1kS)#TFWG>Ln;r)z5xtjJOlugTBT!c@HRZ;6tixH?rfMN@cn{~A&$U?2aA~Pd`Ei&>{ zj|cf#WpwobjmG$C%aRR^EyqDm*@IddsV(jf+Fp%j%c*gO9Jc_| zre4~8pp0hj8~Ym0qE%ZExoaCkg^Q206;(6^IJKUPdx5r%Gty>*YCY;Y~P4 zgE6Ke0Pt*n6fQ6_3iL9cD^w;cn>cp8EsG8XO}9@DVbgE|7A7cte8;`GUjW*GJtnd# z{0aga{O&&2V}vEW@*-3=)XT84%leJf|}achW~;_8QW4t{mD@2Kr*~gWWXGSo9>>@wE(Z+Np0It?K9S-Hcc=djop_ zYn_-ZS^9JMk~RE!IWxNs>++w<5I)lSt_nSWSM4G9)rn<p4^HA8QSjyL5A9NQ^%nH26I>1j_?&X}=Y=Jbo;mize%F9&Pl0H+-j78`ST!C! zkQj-U!oJ7uc&)CUnv&)fA%Al%+Ky6*`}IKYhA+ZQ@tW`oFTz+@q8dNeea)6M3iR`Z zSfrg3j)SNbcm&@$Pi~{TtD$!3L8g&&07Bww5F6wfYF}0pV=l@3z`Nk^6qMlyp!6wn zOp#~mUGj~$P7@jM*Uh(NXiy1tI`QOK1Z0sL6male62Thea7>M4BK)9`yET#G^ND-j z7VB~JRppy@`9ZGuf*{B;A~X7A7nh!?&XQdH#$G7*NhGxtv@P}d7+Z^K&I#)&wP)ap z|4%r%b>qK4fd7K-4s(~5x@ZHqQaAML?&^t5uh4Id25U7sDmq$Om@^VOjH56PKhkT` zZ`Ydc(Xw4@=vE#xyoug`%|`~}Lp5r?WKAoj1uXl z`Y`GuMrX^W|G%=nGAgcRX`8_zxVyW%ySoR1yGw9~L4!lk0fM^|+%-7CLvVM3h2WNN z!a4WemG7ND(^K_Sb=98Ty?X6k{WK9{f^u{HrNxLrRE4%skZ^Nwt&-zONiQeN#{EQNtBIbna-DUGsHid zZ?}rl#6K4OHg)m;iZ-n4tQ^9p5;-A2!&AwUo=}Yb^?R#{Z-N$Evhr>s;y&yN`(y~} zG)QMwGS+b2V6Qdv#(BKrR@{m@BR37+Ky$Cv(|O)Q=52S?GGN2afHe4Ab`$PxJdp5% zT>*jgNtuDBy}F4gP9ZFW$0-$n&yp#J=3h&pG&hGA!3YhSVa4lgv0QPcAI#nQ+v}AsFad#~nO~ z&7Q@8RA8wYMmT_G`cbt`J)O0RDyE?~S!ID~$1zMvi)IRI&xc*ip4YzN)e_Pv0Xf0?&-=B2&t64brDd45v4hN>I+UQL>ivEUhtvJ7;%V;j?DXk*&^* zSiPrg1ZQC!$JjlrObN<{POCub#l1Qv<9Hec%bM)>4al+k1dI)DwM5`WaX!}bWKwnW ze1d+%QJNb`lRG-2(bDe+J8FfYg4AmyzS=#k^hOaTz%fK=*<%%{Lq9)b;M+ zdY#Lpey_eqA&fub(pa&Vi&iF@Xornip4ss(6)%T`o*lxCXoWP0_%;|g=#u-SanjTf z0JlmS9BFO>LDGs^LvgV0{qZZ7F5S^VQQR^MGfkl;g|Iv~y0w5$YX()ru!b#8IDLaT zErIoHcTA7vQ@bG(%NaZa*BofBGOmYxXX3Z@AN7vp91Qh{saDLfgqy=M&X5-f1wp z&0t4wplB*hc6!K`lv522oq@08NZ+z0XrcPuu?JjfX*YxDxJMte#XuYdSzN=|!^qaP z5N?VMgSvDYj3VP?t24?PVaH+jxZ<

z(nUMz;Wp#7p78k9BpR!qX;J2_PTOoWFf>LSOOUdQ3$Dd2rrd>MeuO%Y%O2oe zwh6y+fVfKj(Ql1JI9;GT;=Shvu;SCbG3RIqS}nKf>^aj_wwj}+>e3`MH-XuZ!B0-b zEqcV3vXD_28}Bu4e8=3NC)1Aomb4Cxf@Gk}PaBF;e=n=ZzMx_78Tig53egy+ePj^e z%UaO;B!TcX<)#~Ng2b@gz0$1`(%Q(Hs+{hK59ngPeQAuJ9{q0F_|YX~eFDKZBKc|t zA{gj?T37TbVQ(fe%QR!^*GQt}v=`DaIUTHx4HA{y{@AY-v^`?Hy=@+hp5b^}TjB%9 z9Y%}TT6QP@(3Tx3BB}^YK4RLDL29-e7KPoZ;NJz-xlr%Za!pRwF^O7RLM_O-#oC|X zqksSoC<`xVXF{5#h#U<-!iQss9WVL)95b^UkhQZD^nsV~FQf@KE?8La&={2g>0Fr) z(Jwf(ap!6r-Livs08lKc%a=O@`=P13{yS{qotE^kQQ&w-9L!KGk5TM@@kusLCaG93 zXu>dk%nVMaFrD#=R}RgpzAFp9-M$uDrzX#!?hLZTGJX2HI8Bkv3OMSH4*@|e2>~Jg zT8Zy33ZMd3>mhh+&0~ukSS5j!NaW$|-@%AW;=n>YbR`>vSwG2h=?pt>~Qs&-@y!I1n~(sei3^oUgh~ z1x^az2HdB9QAL}vyO!Zy8+;3^(;@Go%#WV?R>})_!GLdMsPgq-Y=4ewB9OlvrAs}E(%SWyg8ck9QM)zwL2Sg-c-5Z=+L zAJEN;`(7Pw5xj(*(%M}E6Z$@%HrLPg74JEGI#)LtS35&%Q{Za*x-ORwtVH!;Z`;Z< zACXCbYgG@)2WxYPUqD!GL?P# ztHgC|r=|Vg@yRoELw)3&Y1b=CXx;I`Z}rIt7?=w#<%wOT6unJLj+x@Vg9?8V7k?q= z1*VH`??2ub?xjrmg%`qEjt#FpA-og`iBLJ`a@?8nUjHo3XMvZW^THLS`1rVM-P7s+ zZF5+P4^HZ`kmEM5F9PW$7KtP+m-6zLZpKBcNq_{72!rUb#fkj=eO6yC?;U+;kD3Y$ z@QFIK=cMFQYeg2)OT}t{^1xlyYQWm&`_}tUPs~1d&3zm6y9c{B9k9Tu&2vs=TbmQx z)04%Sxz)9~)xpL*$5JoQ0w>WMXyJe_BC_X&WOu*iQINfU{>9ydOkGwoYb?Mp% z&_?z>$i9-#IYW881cv%eHG#xxUrz~8fP;{s+V(a&WCVxEddT@|eEX?O>Lqh4{fU87 zDvaL=f5J+rC-C0S&Y{P9Ai;(9*0EvAPK~GuqMQHDze?9Qv#N|00}q4gT`3>lA!%eaY5ZV6dA{*qS}-;;36;zJ<&5NQ>ANsJg$d8XVRZz<``JfsOOFTb;?ob z6XsMx7Sd*v%?=rp1i2S{Wn>B&P(Y>^%p7+Oe~@%_UU8gvA@44#9+_7l&l^=GGrf!- z0Tod0H_;AP95D7!`C<;lnnru|%t|#f(?eRzIPki}UqZXIi)4|)$~&1)e!;eQQv!TE zVGZkoAsSr+ovQf?%>abkq0GT~sUHuyj0ZwuYV7J|+fBibXuDmPqIR0ttAKX1e8+>b zv+7jU8*}wd)(}4)0NjNe(ZyA3+t*v3(1FMp^^uK!-Zpf2(-a#?gcO>;mWYHI6n!<) z7JTUe^7NBa?q_%|N<4NJzOEHW6kB~CrsX8i<{Xu;!oKtk@g0{rXV@R5w=C%LIvNG| zdL#PE_J3hV<-0O$8YQeh9t5sN54fSF4YcdzekJ(aC&xz=T0sM@v}5CxqcxM%D`Kg8 zR-rP7>&H(u7nZeQ#d15DKGP5shd=@-tds`{vLk<6Nk*>Xjimiq$Nr{I{L2Fh?+ulO z{KVT1FF8MQ*NQ$-G4??&Y@P;s%7N-hCmu|gw(jI~PuHzA(lerZ2@D`=x)px`3QT{F zNUS%Hk2#dyA_D|&x@phg`1k2)=PJVbYZOR0ecN+^Pq%kftv@>+xb}~h&?F-Ocp6kR z`(7m_fd_d_W9Fxc;;Qe-cH?5bzyDCIgo=#5^$7*qTqLMD7ED9KMeUgg!@M}Rc;VuF z4ZUZdP*R)cCsL$diVO#KkfTH3ELSRrgzXELQ+1kLBxFd2&OrH-p| z{3sfyLH5_E;i8TcDcXy|IF#DMxM(WI$x_U=;8h>h#X|CXjz^5C{+88!W`S*2iRnjh zh=xOQNNBrc7ehu8x+$Uy=`jZH&x&@uu*|cAUIn#UGHeKk82Zza;3;r?gAWbnRecvV&;ZPf z^rW2FNa4hzPIgZd5YZ%*gvs>WG+~^y+KolKs_nEOcwYcZ9kItKLrG>8y8wQh!}xTR z3T_H{_O6u$%yjSx?Z=P?1wA@FR~$`03cTxnwuK-2Kekdf`Ei)&?PpYaTP$E=$=7fQMkKO( zp=X>&)WMq=U`E|xSm;Ih{Zsqbd(0t;vNk~>qMy2+>g?~c;)Y!lbL9Pw#0QEs&C%c8 zl`huzeKXsZl|0pUAUjD&!?~CmyE`Zje~RIFDIV=9c&6zBty|BE3uwv6Dk8r$jrMHe zDnHN(_qq@5)`v3P>L;HS-l|IZ)N3e0-&|!Xt36K^0a52(*w zi5Ug%^<+GGy+ri==DIgFFu72q`Tgm)`6ck)5^K%;Xi(GKtA$7Q4ZjU+5qk7;s(m$@ zP6wmODbF2{rl-HQAdo+}e@UxoQ7angG^=xnues-vk#A5krURqIsM2V_ePn;dq`r?wk9-Lb6IdO~6NyFpXI) z&H=SugveeV`2a?J^?hV9zfO8v0pU1)2!WN(DQ3SF8msoy;F4gz)+h_GF5-wcwC z(3*|Wnh!GUy7f#d!jHMMl4+jP0*ASHWhY?Y697vg;Ni@J$u*g`A$5J~%2jj}$G**K zNM@@Ky;1fkso!i4r??!d+|1LtExTYw~FK@tHbX z)vt)srxQ}^lW$Fv*Ja-Gev>X~f7gUxv8F|QMTX{{R;~3_$NvK+Uuh+{OhyCm6BM7hh3mu30#C6ML(zLTknv8PeQ% zw>`N>E#;QGz`zy;oAe%BdsB8T;`xxvhM{bT*-3Z!6OHf`&6!vmCu9GDoz-raIAxnp ztxp1tEBU62I93?#ai$svcZjgb01JD-r5X9=lJPI0VGZdMC1p#?n7>S>L{5G#Kj#2Z z>|5wqyU*JWciFk~`C@fz(d$_lq1CApQ_EjwA-UCtNM8#{^=C@ZU!6JlzVK*o5^-B?M~CD_tErI9CE_zfT)B z4_4olq0<|K;?_Qmv}w*;tbm_A7Dh%drnMiTcyMb5FVG&Tcz!RH`3W6&t(?jaP-zrP zCGM+Gil*hR^rthsc5EDAN`71)+ihN5AEiRfDY^K3#h)$9#vy8*jHUk)jI;`592#7+ z^m)$C-yBKjPE-1T<4w=R-Ikwif=kn>`h$1~=iQ@a!SC_iaT}eZfW0tD^G`Fn8(zB4 zp15@ar5;PcrEAeL>Vss;%qK^7PV05~ZG~->`K1l^vTmA{23Iz!*4Bh`y0q)ZRvcY{ zz_FdNlQ@Bh{gb!^v%7e{;1oR|UDeR(0Uw#G(6Jf;0<3Si$|)qXKJN~s%9=#dw+CTU zczW@VCy?#tl#8C@TV|db(I-x+R)d_T<_=15hAIx*0ly?1@tJ4OazHXQ({>TW5k{02 zxl3l1snwOa)xTLN?2*aQ$Q9+FY?i1V2O?FP*NaD!Ck+b5MTA%QH0jfT4j;d+pCR`Z zP~3=Xndu51l|st>BFp4IXdKJZY*Z|!rHYDlg~X`$ETzs!t76VogngjKRkEeZNi6muNvGpx}^s_Nz+xmRBor_ka;qRSr$?-j$!Q=)m;CE z9p0nA9U)0(@}{hcEuWnVC~z~48kU*k$D7ru#fgGxpdPU6ti9I?`Bg;In@`xU%5J^A zyrI%M;#pW`2>)F)W{0WTRpUW^ z8C($Te0IKfKo=RIyo*V`&wl#!x8u3T1`1gP#wmO^rlk7nYEr)vVA2B*%Mdd9`LtLs zh_6si)mZmXZvRv9THN$TeA@Ts{nnQ(R9gbV<@^O=$7({J8#0ySN*Wz0liU2k7=zSy z^HYCUllx>{-=)_34cp2Rs+SnmM%_<^K;|1~=Iy2cUnmSeW6DrD>gpMOg6JU&SiW}x z^xEXYQu%j{QVgSUKm$_2frjGZUzQVm_m{IL-P-&^NFIUq1brl|o0!Qdy=4h=t12nq zP!$I8YQ>%g(E+&1< zp>+P(3G;XrTn#5CjEhV^zSP^u`^^jg(dn zn>|if_ldMtW&u}Cb0hP}j)71~r`8dd1$Z4ks^T9s6>FEUiJss+2s^ee?*(Vy zY4~<9r?y$`7BSsFhu+xtq&m6o_|MiGD*g23H>@vUSNGRul+Wz&f7G=+N2C3&yL@ZJ zlB>heAZ^P?)_C`UvYzyU%QrcfTB6p>p>E`}>cUVABP}q!ut$Bw=$ktkp@j5ItL0*8 zPceR}&5gKk3sH<7kCZPdrHh0?me{xBmQcR}AA9ir1CPS?`6AlGve7r*`J>oEvIKEc znL=@;pE}c3;dtd6gQsgQ-lml*=K`Ab+20#KF*a`Z?I!0t;T9rB)Dp-HS~h>!bc*_t zX00)Qyx9O$EjV$dX&=bpbFv^5R97idJuB|AIBOdp>c$rKgxjq$UfZJDMYtD71`)>N z%n-ZdvUjB-Wl|D*j1_vJB^G$ib~rQlc6jYPkUZgHs8T*_kz0EN;gd>?_DLcRblIZa z+C@GdK1a=qe8jvAy~;;$4oDYC>h%Dsj3mrLA&dYM_JXovX1cD(0!8C#QEOuD1LwuJ z1AH%Ov&yABuH>IGc_4(Pa{K40g*}+4l6AHi&ZFZ$F2h3mBv_E)DTu4>lA(>Q24O5> z-K(_Z8Z;){?C4C_I@>Vv#RA3#gtJ0AdixyRe%tVzm9DIE3d_I|U4jJf3?J!5F(0|< z7Y%{M(CL$0ndt1J?Z#^c&8OkaIkT?fyOy|)VmbI_(oKTV;A+UFrutt^n z#i;RE@QY_N)fotj%je&Ev!t;jrJR@A7IGQN$k@urS3zghooD=A-&7opU~yxv zL&ZC&8`hnKG6Yms^_lSj4uX~%St3F@3Y^5XA0uNX!yO{lT8A6Sm_fwkdx z=gu{tpeLi_!XT2BlHAgnCU@a^0Z$EC>bn3uCQ-@l|QO$+` zd_#0uo1AD{rUSE-dLxzWZX|~C#-;Bec9462DnFz(r#)hRcNGoz)D2~c)%TK9crI~g zMyWsg%m6>Rz;h`a+WmeS70K5K3Al5=jq~I%@Vk;LHC8DST5G+sHx=Zq+_;`ciZheY zNP#aDZ*J^j{O!iF@;N+gnOD$`6r-tDM9R8Fa7;ut#gGys!f8@@{wlLB(}=UXWFdC` zionK~^t&jgt8x5q{Kg=HtRK>OmYnWh_&J1S%AyCwVU_0TxBSjGSb04Vcfg$>UqrQG zdL(|J#xNvq>fGLy`9swAuWAyUl2@9toX=OB4{e1RP7MCo7K@qp;o?19hdTUR$&iW4 zoR1m<)ToR}|Bca|?VbwFan`cMqKO-8CAsiUy}F_Z(kPh&#W_)2K$;O$kbP zte|KF_)VbebvTM<13!L`n+3vfkFeolFm(ZTez325f6SCA$G&3O-|6rq;KS-bIYUS<8@1ZJoE7;FA%nO-_eKL zoAo_gJxorZ{5AP9Ls|Gc_Vj1l8{?ACE32-KCRY5C+3w6h2F7Bo^yp(c9#DxCY_cgc}r{6N5=2YRfA` zketY^-eUx`eG|OG2>J~SRk|>?Fry4#(M52wCclrS#0m}SnKo99T8{>Nxvx1KeY>ydMphyn#fxfsCb4s#W1^m9(9LMyHVY zPuB;yXH2e?zCFk2-0xZy{UF;Y@!tXfSfc`<^=S2=Knse+u67Pk(8=j}3cBsB_zSC` zrQxx}_?1NP*Kn1o`e~^*PYFh&^}RA(y^~*IDpb7zApfbIcb@P%>}x)9V>;qf;&hGG z4}8&6pnvuWQ`kjJoYx<6EMzV%k zWGyMKXa`f1)?a1a_CPQMVWS5u)}U7#{0f3|}ErAl&84 z4-~>b{{T9-G{+RQ8b)q2c*oB%9Zg^L!dHB*YvXCqzmzC&Q0Ad>9(MD|Su5JS9|=(C zNo7ihTLrIsLLY^?*qaF17c2_`S*UmSY1IyX*>*;@er2v6#KN!~I;pti6$*kZd3t~= zE30_=kdU~wtXR_-fl*^+-c2@`Wc>@nDEi0TgCo!g%)1|36ev@m)2LaC`a={+*c9Y( zkKT?jxHplY@e(*Ds?;Kf@Af0mr`<8dX;}H5c+oFzHrw@aU)^_HzU+A_FUFSo2Jt{$Fsy8jGj}xgpMvYkCgUyZ7u3IBxw4%X!6vWX z#sd}a5C^=a%fiB-D;je$ICJ%@M5*DC*lV84C!zAv(c?5pitKBZk-#d*atJM= zyBOaRtvYYEwSudcV6t55Vk=@+TEDPaYx)mx9L9_abzGlxATzN zzcDGJ6u|zl{QE6E^R)ix+=^*&G%+U7K5sts|yu?`N~DnGKUVRem%6LB@`1t2>HrFvF=`F><)g3UkRpV z6?^ps+K>R?0gJWl!SD02gv|v#Pts*~Ol%;-Qc?tHEWEG8UEuQ~qqXJ5_#~bMJYlU1 zt{oatsRE&Tf`9-GN-0q-|9#oFU7Q_8+i_h{hj3H&Q(1mfZC+3J8$G`P7nCGgYD3i= zG%hPhUL}Vor5J%cpNzjcw}crhwhlX2bTGQBilF-r0~1NU6vqkwRJdSMLjmF+JLMTn zEEt|f1ROQtjXBQGQNRn2PLK81IJvKhZ2TA(>|}*JqTjlh@Sf45ENMH)N6|8VsWGNE z_8_{Kv*$(oFks!`X1hnkOYq-?WFa%Tc@}BW10|*$7^tFCC8}9Y-bdB$ z2%%vOm!!PpbIVC78FGiFJXF5y%KrUhDqLfIpuolF|+ZTWh6_sf|W@OlYl$ZDb$bVxi`-a z19hjs%#dc(o@RD()UFnGa@D7821nkj_Dfz%g5#%|7k&2Kw+3RB(TF7#cd&ERRoX73 zSHNDY;RYS`;d=%v=lHGlg1`Cv@Wvr>ECG^3`82qSt&iCuPB8d`Y zHEw|zhc>f0<9;dPI$RGaOu`dgu04u?H&>N==0{(4yjm65;~Z23G3^WPaih;2^4frl zgqt;~>TwWtNITZyYBA~;!KIv${g2Yi4P=Ii*KX9DQGj#>~03n2ZE2sFV0c>CI*W(Byv zHnLa&8n2C?tN^Fih8f$xjX5@e>#IbL9pLub=w=7ly*3y*0Pp{Iq!6f(<6k)%CwO|` z1v50W%Ykay0Z63aP8AV%<7O(sGX*913?y0b%=4d5WAIl{9w$Kizvq5{UN`|bkgNR3 ziG=hZQ7!=8s|HNQZvcAF1=bXcylTKy{Isu8EQ2}uAu}YC6XjVL)f54cSWy2xWl1p! z)0>V1Ol++ICbpLNPmkd5#^61W8aF`wHL}0hum1vhfqJ&2~p1+d>5l&jiE&&UPG>&jZkYjr@TJ9C@JLKjx!)*stcV z8V6pm2FC=P6Bnq97eM+d-1s9Lu}rQDWMumP6P2`3#Et=zXlo)tK#2Yo9=a9SfIZp3 z3Jv7Q4?ucNhNOfbCmf6x|3Bz^yFc!e$(&)6rR@K)cLN`YMoW+%Hmkbyo50kB>#DBcjZngf_^9TNPPOZ{aY{9hyj$XFPF4tfy+;JrG+ z7X~|0&HIm|S2e?{Ciai!uKBg!5n;gVI0mi%5el`u3c*ZI1RyRE0Pbt&e=$M*)j62R z|1%a@=S23Lkij<#`Tsu_H=qHaI&1(th+7;${EF2tD!1tbKtP0mr=GuWtSXFG zEIlYo{NKBzM;t&1)sOYB7zqP_4f-tpZy*d3V4W+@zq(FL0N$(awU56RLH|wX&n)v1 z@6|y36$=Vr2B4u+{ul5kUo#>=vMCk`NJ0|8237p;k3D<B*>QGkO>En?ZQC=mGr_#)oabBX{oY#LRllyP z)vN!y>bmc$ZcKt{1Hz!FDMCRbLO{U7LnxQCG@_7wf%>Nfv3&`GQu?Q@`y%^q+YJ69 z2lL;6B%IWL9Y8on`2V0CEd%=Bk!E#lRyd4*Kv5y5)xRAU0>S|k0zwX?4#(KsMRWxF z-{b;QVJ z4>*z&M<|1CKN{xII$b=f>s$j8%vZUP`4p6=xd<&r1-Xq%EPyrEo20*yw>=2PklHlY ze@E!LcbInyihirabFSauRtCD4f0cQS3Y+6k_8;c#fr+e?DstfSTDR+nwAbpK%LCrR zyIjShwpRrb?NHR^yY0+4c*It)Z31|TMYPzN)tz`5hZsB|lQJ5scXo~vD4YRYC$|LQ z>CWaIT@?iRw-@^mha^oV^T7NL?UWrA74g@uZt94#@)JO+0PaA7cYL z1b5y@%RYB%ws!06vozaJBZ{udddiU&#n*_+lJzNJmN_hcQhr$Fs0-|VlLxpegOapu z)|l$tP=8Rv4FBZR7#Eoq9a!+Ri)=WIiDV3mRmW%5?t(7rA#;xKK_l zLnF*VjzfwszH79%OmZgIL=!+!`9aoWMHA<1p5NBe*JHd#0w0e$c#shQT=RZJ8#{bmYvh+UI}(1C2OKd>9l*cWift1d3O8 zJWKZ)%P?Sz>V|l6}(*MkDI+M|K`g2J|q@6P3D1f!U zS#JtABVWC&^6ZJuv$OeTZS~-XLv+e~q^LJ7<~izp!seo?E$+6ZEoX92!C>?5TTO6# zK}ydD4mA#Ocf&bBXb(W=`bxtz_M{!ZWFJ241uK+-JT^~+4TMAShFGh6*^d^7Yae0f zi@%OO0KKIel4qlnODdb;xxg)TP7Q(>MzsBrcA<35asi?6Z zBXG5Hz|61lOh?TfvVC6!v#9^(cnw}lJ&EKYsi&%MDPt^h`zIvnUY}UKonYT8S6?@` z`<05XKg2|;J_5uh!3}wF1fm3~8us6ZgBue7k7 z5=nn8K!}CiDIcR4nW=`hcv?^=+IdNEg0@)#>o5%?@N*a>D;< zKf-y5R9Y6tZ5|WZ@Ga77-jCo4uX~u@R%T$$l}3%FQ=aMwM_D51TaHRV@nV!m94d1(x+DrTIr?4N{-3^RGUm+Q%Fl~1sTS`t^ z?6VO+$79usinDE#lcSW5PS@rBlmE{sl;8@ng^(pDhXsNoLz~^RV8Aksl}o*M(!wX2 zQ4U(+8x9-H{lXFUO-(+!#t6>g z;n>Q9sZ!|=m}C`)U8NUkOp<;70xZf^tEeO!}0&vjWZ zg;3%;O#G`}XX^B=$SjV>j~^b5EjfnzAlaghlx-HASSi!c02be-xUbu_6>Tiu;<-+| zVcJ|-g1(l%cMe^e4!7BrT7SN^&8pmQzwY_~Z+~SZP`kbli34ompABjqxuGbR_RVvH zv5*&3VIz}n-2e1QXvCdVdzqB{6=4@F#iqDLucL}HPnn{Yy&zuTYB&9U*-!pLG{h#4 zHH1=0_w@kkiARD%q?B#}B4RZMBc&)`#vRl%Ri5Y;>oOJX&Q2f}Ner@}7o$>Nf~TxH z`YUN0Hzy#>^%H=I@qxd%9*t9$Skx=CA7n-C5eE+Pq9*L)`@pJG{)icQj{8K+2$tmx zw`J3oz`S6wW7LQ2T9>M0nkpHhbZHi?eE~G+3 zKo}!IKuG*+r!uPfz7T;Hm=OSSP1NBbsdx-5Fkg@m8nm~!Fr^p}cmam&gg5@(uIKNa=iKMqj9JtA;J;V{&Ov0-Xz7$->;h@@ z@g{UKsY`olaX}zEC!$Wt#mF#gP(n>;D}C34G@gf^ZjvrQ%?v)UhvFoy8>r#=&wez*h zJSm>2mKP{W%Ds)ic2#!A%CVC_msXp7b-^Rc_tI46;4|6OX;UE(JP|)+lHHx{;$+9$ zdE>DyUpotM(u8KxiJ-igyri99ccgK_;~B!>(6^r5SaUL-_%vVmR)uwu0(V-`Q{0_i z+r@kCke0KvMp!KGvzn_d0K>fGp0C2d8bQ=ErkruOX5DLZ(W3Cyb7RAV>=bX_lvWby zfu$}S*kxX$S2kVHdA(D^by99PRB`Q)h3z!`Ab}2;LoCtPw*GuRfR*%rl;-&k^F#ch?y)j*S@f+*X{Gx zrP~tD96Y(+6dL0~GcFP`ae6Z+_-b6#Oytx)b{RNR$R`ZTDfi;u?V}~v# z2|3>{o1bMi)T`(SKbE=9<4X;?zXi70?*pGM(zV;CgS7F|Jb&((v$O5QnM0T|Zp4`* z-H$9VrMvgwNn)6C%$TAIL^=yuWI9wMSZn|WEvrKD^A8*&1M!|qCZ($Fv&~q`lhS`M zQ-Wm%+c|=wj3YWzC*in0xYp=721wa<)F65!}`VQyiY0=Q&JGt=hvY6ZrlrIFGS2arpdl;YCx?&} zdx5C^MjavFClM(6;TyVGk`(GC7$WXv7F|j?DZX_abF#sqcZ3HWEIIBeh;U%+t`aby z-NX~>Vl>PyZ~4TmQce{T)D?Mcr7Z-AKZ4!Gj>c#Z_e-*rKnCx@vsFrCq<{g-q-DZs za&x3qnfwwLG()eQRP=0%o#@1~W)?T+$e9r$wIH5n2z5k0Gc4t$&Xvo(ee)1W5a{wW#^eUb^Vv@P zp`S#cp4{Ao{c?0>bbKbX`_iJd)I~FCR^L!~00l$F2#yZsN7T6I*6>_cRc3<}Q4hOG zt-r(SQ&~%NPb4Ntp@6*%I4LrVbHi{*K|TSJ7f8 zaG-x=5)5_`i#$CfLBM;)4(gx~*)Qtuv+jp~6K43wQw|mHh`z%8U-kMojx3Xu|1Ty? z!SbW$gn@w2K>b(5ILz3Z*`zf7izxpwM;-qIQPuSS!G>1D{~*>N?SDXJR{tM>R;^|J z3pPn@PX8Bm!aDB%4=!Af{s*&O|KCun|IPn;N`tTd2gzYZu>XCI4!0lsECK`sIS~W| z$3L1%4k86&1JW{m&4^Y>#sR7X!5pE~s1z~H?OZr=6Xf<6`hwedR`LRQc+jcM-pTXH zsuF?%98@5ZU+{t9!b5UPmG_d$^B&yL?^Bc@2IbVhybzA+8GPIwdigkj3!UBk?*!DZgQ;lZw+pUKIymu#wutKGI4o5IQ@nLE+= zPC9%yb6J*#b}>#0OG)rmL#|vu?lR2M?ql=2dzoDoGacb+bd7?6mNK+W9)ZkN4hRev;h83z zqxTMrb{jk@)PyNm?)uBAa+W(hsJ@3(_xwMrun~ACndfFGjQ5-)N+@uxv)jW4kkP^j z;~ga9;8~#0{P?qJlyz_zW*}GdLX>9~0SNlTO)GApYmp5je>%ZZH42{0gtqO*cvF-3 z=^^I&md1JamnjlOOCGZ%OP2z91|ABAY-7PmcR-nC&0~< zCUM!7cOi34s}CbF!Xl3~8@Dqn`^T?v#t662V-L$_;>uKgns(ykt6`6c;UcMQz=ntY zDagQsTizX)E_DN4o6_Lbf)tMdUx#uD-4i37D&9bok~FnJqjSqGn&q+B`TI4ZfM$|i zn%(M&_@t9ubU>9yQzW-T;Ft34ewmN<5!fY8gX0Ts?h6buA^D-K-HQCDa7)sGG%?#^FIpzT&% zZIu?(ESBsoOaZz@YNmK)oVE}09FV~&PPf&yoD>n^243>G7I*mRFO9&u&C8Z3lOHAs zOa^e@BP3U%znl%4K9BzpzO!FnWm7Ris#f73Kx(_zlVs-8A9nJPt>a=@2AHUt>FtK< zKn{mxl@2C|nlE!wM(n9jm0#Y_hepLJW#qf8deWN2<_zDufbta6!@D_qzXyq!Q7eB1 zA#Df@!jK7Lk4UV`S!wO~`xsRcdg(YI? zU2cYsu@c4Va<=mq=@~x+0UC9=wK27=eiYPDR(~|0v|(j(lY6k+l5;Ps5NTV*7vxH> z{W-+!z(#V0v7DIjJ)L3B-+OO>a>inO;V%A77`&lRI_l5tI&b6AwsI=98PPV08HT%)fI2%EoY1JPe9A-p+~(*1eZMS zIbzF7m*Kk`jbK>^|BBL>je6aa1H5o>2O$fW5%X-9a|5+%v)X#0>o0T>TbbW^(L7EU z|4hG)(IZ+CyuR?Z%M%UPzMQprQu=ck(Eih-iO#ANCFkc)*p^w3R{2RQ_eB@4oQjm? ztc9E&A{&vuAifMb1psr#B%wgJ=w9CDn$4Nsy^HvA3y^kI4tGSPqqWCYI%D}YDY`v`bU!7`G~xEG#GD<@9G!4FwQfTjFM&-}PWDbpeYv%wSJ{70eN zDrD9MfWaHR(2;s|O>LXpWa7)ke~*=<&1M=4f{XKnWdIq`(&mxPD>b=WyP97j?(r@4o9W%?Py@E-*Fx^jwCl-=CoE5~gAY@?33MJ{stSe zV<9fUK~xvv&O0201^e(|!W)hFalDm?jeY6Q4v*rVBMJQqIyF=BX`@lIk4O5Ta zqw&BvSPg)w$Jn9st3B`zT}Rp@_bWM24&_7Iqx7pja1MQgamDMAd{7zehQ7z`k$o^3 ze1pEn3le(>IS30yLl9vGM<0}gCL)Nig2N9$p{fX^*g*yl1cSCP1z1AVetHk^<%zYl zUq74Y&o{M0lGzjV{IDk|KEV}w9wDGZm|vH^$bbg~zht@rx>z=}?U^00f^U4i%?1+t zJA52JGL?gj2%5HRNHOuKHO~~NYg$t+;miYhs?lhzka+yGl78INpKuDnNJUB@ zX4)VX)R`OV!I-hErgH89JC$WLCn1nCEkJ+nA%J!o#Com9df-7ZC2hni(-}VfgC5F= z6(H#^m|ZN+RYb8y%BZE^?r@Ub96ar$pFj4%ntJZ+ zfv^spo~8F@{i^Qco+V}P)(K+`oOVg21!M@*w|ivCY)=39Cj()Pdm3SMrgqlZ!Xb(< zKi_VS@p088j4f%GE5#ANMU^WM^|u{hZFv(pJF zf4ps)HhuAYn91!+4=REm7H@NhV9)ek09ltD4z)ZAz7?Yrg?AF=bWu(X9Eg5F62QHo zfSE)qyr76^mTa6v@kXxrYdGQr;$%X60#srK$|w>u2@`pOb)s@@1O)GY4}KywIw3;E zdy^Ta=`-k9;luL*rw^*^8*H39hV1smH;lI<{Q9F&qlO8_-Uk(YlKnr{R1xBz0{L(C zPoeqewgi2E*#C2fCkuKNo$wG4UDyy1lK;6wlTsH~NqoMh z)2$&R=Su^F5kh<2U1NgWwilP|Jzvik6~h2HIY+FW@cb6#adLjdX>S_tbN3c%8kWvmWUD0BfT=GF1y8BnVA&8%NgK$*sJQB(w6LrGJTWM;6v@&C8I;e zPKPp%KYy`XX5X2;;Z5xnukT}!x3KGO$fYE^dd9a(F->47n7Vhm39mrC3`en z9u+7<%{@xnBCkAaXJbI(?L4f6Wp2$jX*BCK-lAlZq1L|7j`F)Oe5>sj2f}v~rqNYE zWTyF*= zJfBSs%o?Ex+nMPW&3Z#~a>pBQje>r)*+lxt4+V^7X>H+H3?J`h>Sffvo)8OI8MKw;?n zM4-xZ)t5;t%;Z`IbZ@HV+{MOWn-_Qk5s1cy z%hdMdtsvPp2UimHE9Pc}@>#Zr_xfTzaGIz+Xa^~et>Xki?Vo%PWVjy?rlBO**LZO5 zq33=}h`VP5ld?{N`$EagGQz)U4^E4fkWMz(B9#PKBuaV)E{;=aJV*lGqd7d$9?K8L zH);>)hQ1~4dtWes8B9{#^#^f`aA^B$4s5{!VX}kVHqYv zqip#0h$c8y-1Fz?GD+ShQe~m*ZkFHEebxAJT8fMZ*%}oG>mM9#evnRkkgTtHgmne3 zDF~o=gO#a*x6w!L3EcsjgZGbcFo2P3Oj1M67NM{+=)iItYQ+=9Xy&XCJw!YkVg$6k zwOQ*v1Lo)1*Mw=P$D?>_O|@Dr;Baw_;gKUl{St-?NU>ma`Xx@#z3MXB zY^!PQwXW^7j)oa+OWI(mqrTj_zetf1&uTC zV`Nl+%SIJb?Z$v7F87iv_-J7<8|gKfr#%++tuenrg-Z!o+B2}xM2-@1m5-%N`mk;B zi7~5(PJ1(2G5-OySvPw7gr{!Z@^^i$$kGBa5q;>4Aq6NqRsMiBF7~fhAm8cQ%E99_ zvi~Ncjx>yom#Wm0v^IojI!9vV9$mPY6|JH_S<*tVD*uiVh^Z?66} z**CsO`_n>EyXu&p;EbqBLB;$WXf@_B(UU^KpFMxNK_UPU40Bhux5LJ28gH_WfoFQ# zviCr|HI?Tyq1+pj0AuY{+)#ah(H^0g7?d3QhwJGMaeb}`5OU7ldIK|BW~}zwV078q zKg!&2T7QZ--vlioZLl~`KeQxHf}2D856GzVb+_5+YCXFMmz$0m;%Q{Qa%I>KZ@y;5 z0UkL=nXdsxBICgU16F1MuR2mwL_buX9uY^cqHW6Xm9`P%Rza!+me#+zK4wEFu6k~f zd?RvXHm`jr!F<)*&YQ#a^;cdSGiDoesyBpEQpV>UVao?kD#vy41NpED(~0F2!$&{=|jvRk{4yfMMtdreFNIzY`U52`W7Yw8GXba zsu+M*3bM{Nduy79BFuxWZD^Z*MKUbCE@{#T#|&Suo7};w1X_PW6gb;OKMUC3-mq(; zQEt@i@nwqez(#*umX|+Q_APSD(@Z2wXWzO zELp0-oDNf^b84dfU+tYy70M6C@3uJ`I*&oO(dFGtq!jXmTxCpx) z5+CEqW6-?h3CA55iK?-GF%R{7241Cwj%Seu-Oy2jNXym}%xLz8#G*Np;2I->wmBsF zJBM8Gq&`ySQ+oqg zXoOV-BwyY{grslGulAs@Txm@H$>VCmf8ZHX6T}R#xU5d|Glt1OWa_238;v0l&{^j~ z;;BTuz48z3vv5=Oa62dG7xt9JqHDZt_k<)p{5oO@jv`}^udrtsQLWa3&s~16t$Ab% zI3yVP8oS=T-fb{2rtf3{b_ymitOF|a*Di02WnBBAzX$m*<0R@6%VW_%84^_8wphU> z)`ZGC80${at zGWYac4MZ^0T3vk*@J;8->T$wKVGn`Cq zI{JgvyH>|BwzgIg=NfegI)>72^hcZ4c9ruYt3lel2*NKOtA6yl!>>1#uLI(7dgqXqG??r6Uqn3D z!<|}{cgDT3$@N^Y_s-1ZZEjE)$W`!Ay(jGLTP(=+>!V7K;$SyU&p5(qH~=xNf4DOc zr)M4kjilW!$zpbrn!ZOra13JfWLE-qWrJrSfV3|{IC46fE0kh<35;71=anr zKbN>IU$VFi-pq;58UZ`hV`xZ#iH=w0#;6=!?vUJ$GB#EvYJxU5;tE{=IN?3S5;x5P z#>1pbK1Y9*B2Yg-XN{tt!5o%KZVf;V0eOt>XdIyrzh3(QF)T`v!uh|$<;%sPmk1CH zPwH6|^T&YPjhIgXRzh@ihK??lcIDBWjz5ulLJ@>|b{J@Z@~G;Ag>+xTWiiSbkGQ3k z#MQ1LzX)@0*GCKo;8T>Fm{z}?UkDtsho)bpcfrKN>nY+WrZ9S>ukp+p zZ>sHbwpZ45k(z6L3v8HfE>kSHx?G|Rm?(c0MzJYFG`G?OsFELDFI_`Aels`CF|9|1 z4#i+OLV~rsTKZ#Vmu4GJYL8^!C$>>SLns_C9NaGh1wV1&Mb1LhrTImGlET;(Ilw!- z7+T`cN4PMsBReFH$f?dOyu8F@&V{8EgW?5Uyf&q9$UO1xr*tRtzTpLQ< z<;6{maj3(aV*}X5i3^UL3fv`h6$PkuX~mboGuod;0FED*OX%eRKso-b73%Q5So_yi zYlO;$u5Mv-leqG(Zo|4NaU{Fy7d34MGKZgUTQvOgZaldw8P>k==on3{(D>mPP^#aP zXfhn--0cZzFqf{f_SxI5z2(+>xJ$_kulFJk3~W5&qSKoYs2yH+IQb&FSnH~6i)xg$ zaNSRj04{74&RUbG)a-*UdFc86(BdRoaG4I5#~@q{B19i;INlLF5Y`$dKNxUxAW9O00oQV82+f;=V$|5B`m|`_W*MX=x0PrC zIqR>Kx7D+s&@>s3adJ0dbGxniM*i^WY?2Y^@1KHxt`|B&z zNCY*FMAUv<`K!UvAAM?=apsNUm8(9J_!9nuPpa8G3L_V>Zck<_agx;*hRQhbbjWM_ z05e_H!e-hHtcS>)7u0azAD%6P*=4N9SK-@1u|QS&QaDP|(5+L)-&)gX-Yb=Dd0$T0 z#X)1X?e^xqqwCJ89+ncO^^T4LDw+Jy4?Kh9U#u<5ob(nJ#SxX4rCxp}=O^`*X6>ZR z`4OFtDhIUJsYe}YMM?MVdKtvaHWv~g08X8cd0ru_g?MbrXgm5GXeoY>kqk(k2bl76 zTB?0BD2}o-=0=(aNj5F)Z5Db@N4Q-?m=?~)P?j+sc>x!&i_!KqF0${%b zfpi&;l%$n1aD`YUNz6yEL=i?36{ZsFp4AOrRM&LaC9!qn?O9+m+6W8w_isTRes zA+B^LAAkC*-kV^8C(58e#!}$u2)ND^_~#r;;;$)_%H(BOoGP0VI?L3$*fsc9ts}4O zB%Q2n9ZAJzP>(;5WUhkrMA|Z0$tju(8(C&69uisvQ0cQt(&J=-G}RiiY!# z7DABZ(|vvVcWF*UGxwuo>xAiKfpX7(0xAkx6)A;oH@s8^MH#@6p>U;Kxd3tiGN0Z1 z&g`68ZUm$#m%)Slc9Uwd^*hzWg^LoYGEcrhA9_S+pPAV6w0aLwD%?iUL1nZ;cye%U^=x(8FF4}> z!UAG~+8`OD0LIZquC0%=*)bIR()(Y(Yo1_)4BFXDN+KvVd-EOc4FK__?wTKVfpd@+sw6Hb`(`j6w25xU;n2i6 zkJg8`Oz@&98e+ZBly0q1Ka`)F#pQvIAV^4w>5YS!=ah}GJN8S;SO~Hb2EJjW`Xn!r&o{*)WTe7T+mx zt1Oc3G!6weqTJRv$SThn9>^t~bfjbTYJfE1#P11>`cs|0hr%$fYNM~k+BnlA9@4pW zK%65;?VtouTCUMVj0{3Bs%{lOA?i|_7>;u>$syLNdH7OME}*Y!h-pi^C>Aw5M%Ek( zgBE!+v(h3>uNIMv&e5+;<*-~ik+Dn8Pma4nGAWa(YgD=lJ|>znK&vrb@W{ic=*YsT z_{g4L8t3|eMZ4zQDsQ9k2%1@UbQIZ}DyA6R0}f^69c%`6Na17vr?uP#d_RgiBrtR< zj}(sxwT+nS0Ggvek6>}e5A?Km==+oqHLus#iQeR-b1Q*4BeTHFI$(XYp_V&S*x#y- z_>r23-_<+o>oVB!PHLm;(9TFm4icqn)tZETb0||s4Rdf=RU0#YJFI=;zn(YdJ0>{G zx=6HxK*`;bL5ev0xW1IR>=hs?%Ghpng{2j43N{){K#ysO`D`*e+STK0Z_x92lvlXQ z55_y(-)>JN+B=FX6nnenKo+Hg`>G@H$0SBl+(h~#b!{h{ZLP| zM?(e(U|Xn)F<*5QcqD*h3;b5)myfcIw6MP4z+nUQlgD9;C961!vgl_F9_iHO_g}4e zYh(0{!TgAp)N}$@i3JzIA)>|()jPX+r;mRo5YyNB82FSUU@GlmU- zw7~nu)+BLWm1;&%#Zg&(6ig8@0k1voUMrV8OWsVbB+@0uwK-+g#ZDjxjZ2t8Kbkdv zPOl~)F(Bje^~0i96v{F;N09FJQf`k?IOPv_Vaxt%z*sq6$&3g^fBkHO2+|fmt)7y8 zaRes&-XeLfkxl`7feaa9)SEVj6wn3`qhgRK>fyqE(wzxOGP`fphkoGJk&Z!{d@=e4 zcci3IP<+yftjVH#xzZCe{opR2+-2Ur6;G>r^JysZNIt|(O=@oCm>ey$xbDFDPR;)z z#_j8fL2$92ESekmxw9E-h|68;RN+#S&)3cn{4Jln@{xXo%eQxLkBY4>UX~J|@>m3p z-uL(xXGT#EbbICyLR0uw?kQByn=-Mvf(4(M*f54>&jGvn8Uw_=JK@903gM{CApVUB zH?S-(6xaOC;pzQp)KI%$mh>`IEXb7(jjW1q9~#@wJ3Z8)-xN_I8f_eLCWC4+-AqC$ zD)PakI2wj!7!>1~a1^6>CW{5|r?rV0N1;n6F{hf6ywcp$!PZbV?mx%bspmb7h^XBq zL%tZ3%nk?+Si*{)80RTPM_lE(?h%a_E#OeP>1pbsSh80O$gXjT7v?I|cRY=8FQxg} zD=uEl_<1tmLOuhj!FRw6C3@oS$FR3#wIh1oajIv)A8Rz+qhZ-E`-li|)m1o_OB+jK zB!Q@0qTSu55{B+C5JR7ifzdD0qjBC`7Mu~9(k@pK4b#6}) zWEYr$orLFka(R6d7v2qM^%1T#N3S0gnZk21Au$@SPnN6VM?OP=5B>U0?VVWxN4W0bmREk?cf1@c2e7~U zh~_K45^o!4ow*3d!NZ92ENphij zgj&9NRUA?pT6P@ZH`gGm^!qmQnKhrs7IfxNT5P}z>FzRhY%IU&mXcx3K^JW0ZSWxI$-jQ>r(pnZNOjlbZm4iq4HqS}!wo%CkNuDB$N+en$ zle;t~zOkk{yrTvv{)~*`i51eLf_yDsG;}eTIF*6VxRDBYi7aBbSw9tgw`p-@tFBGk z;sb+zl=Rb7S1apQe1Dmg?=I5rl4AM9bybTXOqpxMa6Zr_($ru`dvUTH5<@w&3tl?B zc!E({AvWF$;EKJrJh#?}kG`{#U^Xz^*sxGPu%haE!kg4N90h_^pD0oqgO0Dt%F0V5 z(b5P_b{qgf#7JT01%m~krI)E+1DSZc>oimyk}U8gChI>+L`u;R{5aUdD|wIF8i zPf3_1W?`XwG&vf-I}Gcvq!n=@0#Fd0ToF;JBfpFJIGQwRdZsD>+4eql8)dmOtyG0l zH8A^ZRSPTD(7Ru>gO!F~?snKUzRNCAY5S@v_yR!t+ahSzvve{nd%Yz&4(wKJ+PZ$t zr9y?g%Qqjw`Sx7Os!cGjs9{zSP8cOgQj^ugAQIm2(qVaH4dsL@aHaG&Hrjo%8vmg$ zuGiD^W@-i-ci5$KdMS#?vQ!!Q(s;17Ym>{Tl{!&VCz&~uuIsyY@UD=?PzQZ)9aP!XWOJn>ni%o5t0}Qs zYU;+M8MUC{T!RbC6W)bqqhks|80ZlAs*42h`v~Y*xyUkaghEL@JNxG_3IiP3*CN2R z)9k(LujE`NT%xQ(idwkCk-@h?y^SyG5DmldeMhA`@O=lV`{ibQ@O}nuls859CV#vk za6_3p!2w*A;+8{0VXxJ*ch=(7A5}@3}IzVOYW9FYP0hfdi#3BJfzf7D{GWR7ZLq4iS8ZERe$; zDv+s#5vN7`7C!pRw8`r6I#^jJmo*R5pK3LiYC2>XQ}vM$G;UJ#{iKko6Bvc$fOptE zUVlBs5uBIVUD)9%x;x4xP#rd1=6Pyu4msREX};L_J@>Lxg~xIJcV8t~6l&4X`iz-Q zTaB2a&1t&3kdzmJdJ6eIUVD|mAeF$aT(|`O^X`ZIN*~)yt!Y{TmSjS{_eSog-CU`@ z8|o(xgZs<}WfU^lvW{LfIOR4i!1p97&&k3HQeGAx@G*x(`3|zt?+HD67aF86t0`VMSmBl9vZtV9TV4BsyebM_^3_9*&Fw z>Er5JGz80*&S;@{+Lq=DK#Vw_an)WATbU109-`KONgwy;Rm4cZy7vRb@&RP`keDgSalSDPk(T?XY zmx0X7z*yk!)m`@?PJ3$wKccNx`paPWnuqVCPv#nutq!VhH?w-?olfQc!iHY$@W%73YN~RTMym{e&<5t1K&^=nQDF z0QuVXKy zXX686|KdbZw~q$%0Ku>xc9KOJx%dDd7bQDsECj1*#?%?-%#K7F<=;S(JrQOC(OWo( zwIDCTEwT6P@sziY(PAjSc1Ql&m3Z?qOl`a^FYq>Td`o58UeL2^$bJ2lI*n`~k)6zB z)C0_+Gob2D+7tNg$I34sFViOm|e02!Ija zQ9f0K*v9ab&_oFf(rrc|j~@}haPOcC9t^T5v_q<;Dh4nBJxJ-{)(v+L>2Gl^Wb=dJ zgX@%l8EG)er(`Fy-O%+R`f0A)7ZcdOKa6<2EbvjO9PPp>g{20^uyqSzYOO@-hizJl zNS+RKauX3xpAJ!LWZ+H_4@7bk^e?E%{;=mqZc1%RY$TjkXt?0N9P-Z;wc?nw;wS8e zq~k^XdjZg3^u&5uq0h}Fs-?s;LL(4SHO)Fgr4ezN^p@_UvUd;@)ZDCYY*W$YVg;aM zv4zA3v1H>3UpaE0*cv|In%oJ$Y0fkqw5vt_7*rTal*DG+#%g0PA)+T3PPeXUVW!jG z`e&8hVA@EbXMX!RAJ&}PQiw;m(U&PrgqP;0eh4TQHfhmxuU@f9|4mp7kp#UFt)g-i zK>FAl4gQ+KFO=R9i1@fFNt`F6m@QA7qgd@oPTE?CqJxgMwMcR62>Ra5AkM32FGxYh z3|gXuf3jd;!42&fQ;1EosRS)qlyurC+BYu8*(%zrQ;RAL24vFuvWs%MhZ;G#xDYCj z9{}`*BQRZXLzTZnZC;LzA+CqgGuO0q)xMFNZY4-d9hT8^>vkA$wlJr&DE?EYv7Fo5 z*~bqxL3=aFhkmzOkipE91%4dv>Ft<-O%jR?L4>N3VU18dR7d5 zy`{|4wrTUGuIRx#2_|~YP=dR^ZWlUH-qkNSo4?aTTc_)-v2blde-_`+hhusP1G3a9 zzGg*$%&`Ls;}#qT7~DiuXkvs&-u+;W;+CmVbn)%R?K?PBhT}DU@gHo^v&2fDZ8Ygw zGi|Hp%rw@vVCAB#jL2JU%M2>i6n2sgqQy+`LJpzU+UAtCyRY- zBqSrEb+_pej^Kj&5xS2|NZiaq2?(Dwj=gWTuLveR`z@a7hfz5*IAzp^rlq?9C#=<; z&a~4x!-FV5tHJCh6VoimGI`eJo#yY7^ObE-FXyCd>`@+j&673b7U_EquX>lWs*=pp z#3)lGXNs!2WH9j>F1`yggBub1uYpKNN$}h26l8Oh48T3Xkqidz z)dkNtdI&Gwqi`^wV!sbe}1AV9?@?x)$e* z_h|OzD9tF%KgW!elC!c@9+%QuimT~02>Vd#+Rm$0w+vY}V-0FCt>jI%i*<-_ZeOaY ztBAE`&bxw|oA_;&bDSnKlmYs={L48_&3Mv>ib9J#IuYj#4El;(X{iguoQ~x+o5u!* zeN>T%A6r^jaDTijgaqh-gxA4&!uy|AI@P01rK=9cOAgI!_N|&GMfJln>30D(U=)t{ zZg&P4Tdhayb}Ic0R}14gg^MS*%4xoClI=sA%GO+twCv{1&6-}DTENOZwwviBd|krZ zx_0Kl6*7JdzcZdah8c?efzi{>vl;$cGEJuH;-tToK2Z@Y-XNsYcO4@oTyqBdyVuf; zS~n`zFxLZ*4m{m~;||PxF_d2xL_N_az#c*RBLnH=6$dbvk9sM(7#{2+b`LzACH(vd z``i%x+@SYH$TbHr2k?N>BSr65nHjDNWb9E`Imj4c?2++0ZndB?p{pCZ9-Z|Ii$01! zvVU*#LvT&u*s|hJKFY}APpi>J9c9$^gWaq)dKwqtp0|2RCo+#~;q`Z;T5W8dw6#vy zjMD;T+ddNv7Ip0|;0p+#07loytuycoNH7 zPZP^(Ftz~EM~!ST=58%`bTnA;^3hpqii7hvfz&7wTx?z6^wT`2zNjcgzj@V+yF8fV9#U2T2zNr`b^KCxG61-PVr1jGM;{X3Z&fn)t!NWGVSv|39$gm zcBob3&=VvP)2k8fY*TNb0l+oBY%YKMjTH)CZ#4HeUP;|&Kxv7 zSwT4TJDDNq&5~pMU`Y2+2eYX?!D@cWg$~#C1>h^901zi$cpZtT>|efw8*>xfK4|OM zCXmBE?L+WQp}(V44@!}GWWXHVr3MC?6N*4JKL$1s7KFvRkp+Q`)fAVbopR&F(AdY40mI(yc;O_1koDA+x1h?SsK1gua!9#Ef1b24{!9s9% z_u%jbZq7Y7-{S|=tiATGUDee^_jL8DF7CJc0sEoGK6J7-H&j!b4;po88iAD-gkD`2 z!*uf)fwl?`-kw=BsmqFSDNQt|e!0{GJ}t6y9iL-Hn_;i7Ku@>t6G_KV5nZnhC>{{= zmu;ne7xS4{nyT~MNpV(yv(xvPweBG4Mx5;kB>94QcnbDBF&mhd?5271@S3j(J63un zN4X8f@3zmNsG@|=-rz}nPC4Q_7DbE`e>^*mZCXB*->SPJN9dJG8R??`=|O%VsC`VT zoqQaxYv?R~D{p z#%*<^eP5R(wXz5+duU12fM3bj4{JU10`*~0Pk6l9zmlpfOPs_w^hG~53msK4ID`U^ zSvE^O!WY5N_*MAO8gF8n_)s2vUBIduAP|eNbTf?!uiS+_(w1h#4r53B;1Mj?K{%lP~ zLe#keNYlE=UMuf`lb8=0q2BCj3)#t*Ur0p?bbqfXyDSOeN}9oILkN|{H# z2<6b3SxMGcG4LY}YgMbV_T1h}*q7mO+nzD4(Zdc%Sgc`3BMw^ah81M>C$e1*s~YE? z+&-Z`k@Jxi>3D(f9!36IN+E}WkdYDz3k?P33sJmKjbFJDjn`Vh25Bi_0HefS9mf8_ZSy?lD^|1#>PIK zyTxPr!)qy}>g;;2DH^J`Vmgc{h;!Sw=a)2M4*h)pGTv9N-uKZ}$@=!30XXxk)pJaT z@0=ewK`IYIP-=+f^p*y1uvVZ>#!CN<^;b&gBcU6ZWaY^;AyCS%VIs^J6| zn8{($aTnY*zL|Q!LM@?l8#WhEL@&tGq1NLWHdc4+Y{l@yZ{bE})^Mf}DX%ZAQRIFx zU_+!cF4fZ^#m~c|6>8l5CeD?3BsBk}$(a=M+g&{zGkS}~gO8v-H6*PIZfvFjZZJhd zrLCOn2Uyfw`|`jP_;fnQzSc7wwk;w4FWZA`>ebD5;>O->iKgQmVYxb(c#$#!7o+yX zTACd1&!DMwO3FG@HGY>Q64O>%N-TxHSjsRAqHo%PM}C|$S(UNde>bvjA}^wLQujhD zk;sLGL{Z;O%tj7`xn;N5{Zf$29x>wuF^3k;f+BQI@)u}E>to7 zj=}K`j*!R6eqX%YMbe5ML`2zvyGIRPJGMhR{kB0ciW`q0`P_&f0jN~seQD5YUsdS+ zGVdOgYAfj}rN+I!BOyNwDWlVUDBbNfy9&mkL7$Ma6!F+u+06=OlK~Ns9Y>exST?$s zHW@8+@uzLXQjYj$(Kkp}f}0E0X{GCMVO_`(J=qYwo1~k^Lc!9tCzWH-Z*zQkjG(|5 zKv-MIYRCx?T(u>mn+e35q!fZyQ z72>%bcP8Jxyx&DTFWHvzQ%2i%A<$!s&xrso_>pTwW$A>2f_!hV6|9 z1w{#rZ+CxML_`Iu(M0fqEFBWrGKmArlgPj~S@~y?BSHHkB4mFcPYt92e8}ZZX{HK@ zPjSMbD)pJ|`Ca0o*K?>5Zf(s-=jV@9n`Gnk&1FfeX|AOvaraW>vSo%g-q+`@-}7kE z^RU6RZ7Wki&*SilQoQ=@f#bwM^Ur0Z`CPXT)EMu;qovv)%7vQ5?)}(JhJbfXMW0xc zdIkUM56b&qI8rs-9GS=ZY4=9|u zRSC|%{LPR8s!0>5pIB!3P%}5Ibw%yp)HmERPdi)+YwLK%-K;|(fV@cG)im$Le8h8= zXH&c)1gmEXDxXqdY|uNtG2Q@NSAMDjb_lL-8vI7eY&msqNrP_If|W!@Z7kAVM}IXd zwNUM+ML9z7cMm zFt|52#vxug+dSy`v~<*ApB?nv!N&FxqWHw8Eq%Ae0EsR!2-(K>%`p!2kOVAgd_Tfj zcdfsMTB7%AmP$QxKD)iT=h%R@c_0Hx!wNG&yW)G*rNLg46e5X^pD+9%#pn+>t$znA zVsYFj&P$3rute)9VsK20@{r)P$Qmtn%FBdF64MuO%4>LB-yww}{8lFLE~W#mX~#rY zQ%1r|71TxEQTwSB&^AI?Ly#=%aW4k6<-(~={TnF!$> zlRz75Q0tyRmi9r(7K5V@?3&Kl*7BG3Z#S~XbjcB+>_qets5TGNDYk=<>oLyZ1S^)` z&@D~~BHzCs&SK^<46N~)m+4F<;9xU57h4wIPc(Uj6^3?D<2A+{N-}K3%ViOmV^nLE z!jhSM%v(q%n*z-QZ7Q^repMFo9w0|}Vgj zMrhwuZet0xUp8kq$9%ADun2tOvxJ1iC|5Q8bsNF`KfPF_qWj}A6Ec;0^OjLtB;UMv0_|F<8!+i7$Q&?Bx$zN~0+Oj^5MZT=-=8nWUT*%Tj5)b%9 zJu8xpz%JlMnHT!#%Cm|u=R?i27Bj8%TQI7-`zmq#1A@+Rh4*Eqskz@}EUFv?POG6j zAXd`m@06!7QS0*dYyC+REca$g1G-qS51uM7!=`I&zRyueO|Q`b zSW&4>WE&;gJ@vd~0bs>;RRRkdUj9beB5s0!`f8}dBMep)a;Cg5huZAp5&&wrGNsB^ zVxU}zQa~})zDs+7P-*geeeXrx&X*}aQB$v!UO#|kmisg*p1x)*2%(_ByMRe` zJ2BMlHURpU4@|MkC!~`OkZwy~mQg`>NcDF_S~%LKRD8YGJ_3XTIdnFJXnKJJj9OZb z(veDNfjv8d5PyJ@5r~RFax*?fbQsl5>ISZPXneK1GCYMJGd7!4824l!6V!c!!Pu{=jRYsF^V zjp`Dcwp3?@{WHE8&O6sC#%(uDG#~`)Ljaje0PC{x+=y|Z5f?h)DV)%v3aGijUjOs! z?JC$`)l`lfmUm+-x>QMHBquTk?3}Xc&hI@97VFs^A97U?Ee=t7I)#T8PjKb!7~a#5 zbMDwQ!eOihR=_jeftb?^^et2>j>i-k`H2WZ5^zuI7_UN44Sg#8+l)?KchqQF0v653 z1DtaGNf=&VdD4JBgY?IPx4YzV6`5^@97MQwpceS>HwL&EFdQZ@8Gsj*@rz2NZ1qFy zq-RRV4@CnaYo7`4km5lDZ5-G14>cWa?ALYs99OP1kC~79pi|OobK~xr^X$p_xsu_r z&OPp3slDVxq8@4MYn#Yt!|MgX-O3nDkK@jKl(cLE*y)4$$J2V!~tIsr# z;hzmOoogF%W%TkV`m5w0UO)D&n_fqBJaS+2`7EmECY#%M2-ETz*C}=)=gDhiY3DPj zON4lBHQ%S4gLFMePX*QR4Ffsj9wNLLoC3tj)v!zAu9Q7?L+#?ym(vAb1t@=IVBh(C z&Z^e1L#~$S5QtD&ka7-fa7(DKm6%ej{l&AfSiIPD{M*iJ`Li=B_2Ybm+D&D$T~h8z zWS4O_eXjKyiha?^283PU2hS6$v7C)KEAI zND{E)9ZT-r5WgM4teKk8EDfLSSj6X=vDMkXGLEn2Jgz2xp~n4NaT(Z( zAzk4zNgXuJ9L;Qagln$-5E~-0eCaNj(-pb@$8^g9_I^wghJ3(Nn=$htP$Dm7~|VKnf(Z6*@IP4 zySuWVY%sC_`s38-QW*=qMQ^5!)hI08kg(8tIr4gv+d^O>)sb#L0k?!0+oGVzrN~mD zMhn#PQ{A(>txSzGg+TWW@Q1Rl+6Ck1B3PKoJa?51wsB_uL?A`As{%|i6L9?ZsX&ERJ+zhN$dE7kVhTH2RDOH6!mz81|X{U)=~GlQIV=)Pj_YnyQklvgysY5T!fS zaqJmdK*P)j{>d8y7xq`NsKIYuFBFsIQG5d_)w1#RwX%iL78%$x#v)tbgkiCvB)H%1+P3ufSHxW~=cEZhr-NzJ#WG!N?u1F~G0l zD|eDYAkGz~6^FZ5`noZ7^T>AeN_&wIn~L4X(~m!con#!x%l3C|f1Hq5_Or}QuGg@D z1iGCxW8pb4_uquydT450T3H-e9GLI#9nMV5jra zB}#P!X#?4$x z_Sq#Q%{9;GCQR3m4~Ic+_tf7GXqHKXRs(kpt?{i@^>kw-%SJ7NiGy`1&Gh0{uM$ck zGE0`3DJ;<_&<#Xl9EQT^aZE5WtXkw`s52VGKX{sy`4Ame80YM-WGs>~F<33#Q6<&X zU8+OT!;5olrRycShPf1-mr7A&YxBXB5{}o#*QPrk>)>Y$a4KoXd`PqhCqV8$3fGB7 zlB~YDes(0)-bVUF5%b7}mp*S2xMHO$lbTlC+YOZFq|I}oYoxGGNuq0QL=T9;uu3wl5SN!WhtI_l3_q2#_ zf4!5ez2R%*LB0_g-jqU|62SQ<5FdWeH@6u%9w!lPllOWooCjX)Jm=>c>7JJM=E@7i z5vJqe4zXBpIH{(lf%z_dINB~>0@oW$FSR<1 z#?IR&UGj*5JZEPGG}mq%eHzB+rx98Xcg&Wm8_?`k+qfGjUNr48>aG`$gDwoVqLv7x z562m4<{vXu%?Eoy7c)5eXX@cSB)=(i=cZW6Djp>`Le$4oGnz7_)lGZ@Hq;EU$HlP1 zl;o_auVeQpKwfp#=RO|%M7xO!>07q}gkidUo4QMa{N*%UFj=?QG;eS>|=A ztLiy=#ken9GmqP=V{{^p01`ble93qbxW8^cxn7hUpF_nmpQb1Mds81nt${>1NlQ>` zE~B(f)W6k)WqkD#R#Ys$;NpWt|KshqpD;tMa-ZWHfw|=YZ>Gjt%;WF!t$u4;7JK2HdFY*HYrao8jIKs|DF#~kgd!F)QB@|Z zKY9Nf?0BStPe|;`bHoyrnU=gs3s)~@xbLdQtwaT^y;owoNX6d89x#1W4k#ZFW+90_;+ zd6ZaeF{fu;;9LJQ#q=Fp>3J4S9zxw{d8#-{|$AHS}{6cN8swgbWX zk72=3qrUj^9i>nSJ#Wx8f5Bmlc1O((@kRsHTB2_Zm{M;9qOT7pn&-K^+*#%ri|l(fGk9=@{Rr ziAWJ*16cV6%0~3y&QSXHMKc*B)DqRWeNi26Gx0s$Hh!IrQg6*kowGhXYOKId0k4>h zdv4Q~XD))cZ;5Ne4O&3g7i!CFXXK3*Rc~hocl1~@w7Q04+~#D{W<&2z^G&CF;Ah$Z zG}V*lPW#j)`j)dMR8<27+-Y59xJ?{H#+Rvy7X9F5PynLv+ZW!6U0{>0w4|&ldPd6Y$&D4M!r6FZ_d=o?0y zbDctsK$e<0eP&}an(5{am-)%s=BYrCxoW{RM+WTN`;e8uS(3xzGCtWY(j4lHnqc!C zUL7{YAqzy3{EfctPL?uPjj3Oe6B4vVyYQuj0uoxA4T@t{P8y`SIA+7tyTwA%d;4XR z+0xt9$744XZ&(%mGF5D$W=BVZoQ4@a&dC(^K>R>P*Pq1HBRQjI40bJs>2KdfPxR2r z%#?Bmw_j;)X9I3DBidNDyixj(3V^43t;CH&0XDg5ML#OyJs7&q;`Oh6q#w~GN!!1} z0YEUEB<&HDz9M1xQpFuqnt%V0eZ*S3FSuE`qdaM6z{f(zHzC(Abs^U^AP1M-H zq%pBq$=zaOsPJv**Ezr04f?M(cn7`EHqeLa5Q2V@3GOy+BKzAaMdU~qq}P1Isl5s z*&pp7OdnpyP&v54RBv0O<97O$nS~;NIC7+>!0^JyA9!fSgPn_x@JXN zHNa~EV-^{6+G@mn%CW3c0Ora9WuBZEek9|Rq<|%BA16V}0*N#qyiZrSzq7DXyni4h z=}jgre84gBcDmWrb zyCSjv)ZJw8YYKSKrR;@VyoiRqBq$GWoFCfW{S_^9MbWk3+^ySOA8G=Yxg zBh{uPQ%2)RL+!d9waDUnn(7duPph&48NHY69_Dc`5qiWy;#eO160sY6SRi^cC4n%n zytSOyADaq@ar?|i9>e2FsH*nZbl&N>Ml`>2D$!3N;Wc^bZxVmjO7O`Vx~^%=CR=gA zrDKvJG|STV$4J(tNb(8OW;$RU1mzBEHv%|Cme^^7Zyurb-{(5zLnV5-cS7c=CaU)u z7_vIZLLg`Vk=R%$icv(Uh!G@`v6T4vR~Zwq_KNB2ijuU|e(9}C$EQnKv!X|+22{S; z-LIwiv`f4w?cJqnTspe6=A(y1vNYUPrmN`hS1RYZ%gkeNBRNliQ2MRxS~9HLb-JdsV;Nq7n66FdbuWe!1KwwKEN{J#di z-447Tx$oQte-FcEe%+~9UtmOKYtCCB>ABs;ON3}%Jt8C6>BUR>Pr zp$bm%mQyCAv~fS*26P;oi3h4h2QoAC>^&q)(wzQ`^P0LVfBF~4vfs}3f1Y~vMpLu-`e9U5-+5JIwF6^A;1zszAZ!M zAtMpYPaJFSv1@yAzY&Cqm;&CO@F1Tb;~{9;5<=ftoAuQ;HOmKf^#pl_`KKJpVj`Qb z$ggoo@qX*M5e4d`z8_q@k;wh^8NCk$bI*-2@q{O}Z|+Nrj{4A4{&qbJxqss)_}#2u zeMH2RU^+)rxXvOoP(ya0@DJ=LMj9lzwQ7+wy7&^im?;Ft1gydIRB_x;La2A#KhTU) zBtl7PE4$VkNc9Av3!RvKP02qo2r+b2L)Dv}5y4%0ct2F_5wnhn-y4T*;m|1Ko@!@G zX|E(9>LP}67}+>)FdhOkmEG5b)KzudC#3rAh^gLncq2s4fRevSWAtn5wj-VRXh-9y z<7f{|6XHNl|v+DQN zTr_JR6iEl`@+$`Sb+$BG%O9OXsAcc%Cc`XIH;J`Pr%z{Xjfu`(uSSv1+r!t%;$&gx z*YZmumj^9FDwIp#5mIcb9yoNtQQ**=>V_=Rh2)_MNQrf@)wB)AXdxsZe{3F%(JVV; z&z*7xMe`I8a$pUQWiiSJyJEW!q7d8F^hH)*fvcvY+>cW|WZ1aBE1T`bB6R0h-<}au zb|=9Hu2k?`WKvz*1;iPu2&@|^2JRYUIZ<^55Mx|Ak945GWrl0LcTj1^#~mtctdA5x zcI4@KCn$KF)+mH{lcrjzyMSk{JzryRsr>OhD97<6ryo~_Pb44bE*a`QX%H}90jGIq%fNAQS<9 zL>zM>&i9?rf;1VOTo|U9H%D}_>DkUKA7;R)gVWc{bXQ)l*aDDpC+ejg{NAQ`@r~1M z6!uftltJF|{P!u>lcG38O(@>*9{8*#o_{X}_f)9chXD+tJeL|?0WhDpL8u$S0+a%t zF%S-b@CC+`a};@zpfluwatTDA^39X_5V%|nfCQeE0N{ZS@c_smJlVCMqcHH~U!3Lp zkIlX;ql@RfRo(#Z!55MC{rH(;CPa7u9lA8C*WLzIgyD}?`1v-ccritX^~BJY;-=$l zmUC;-@sRlO?C2H^sJYC$fZpCvnEpqX92CvUAGi5 znXgbG-vX368>Zee|Mc4(D!2(e)#!_ju5p1jkoF4dQJC60fyQ?d{8ByA(--YEhf*un zM;>*})pt15Wsph#n0IJWVi_D9996k1$w1&GqM`x}7oOoD2F3EOGupysjtkxJ@YQ$P zv4;nLsn4%Q{DtpAkW$m1aB7Y?G(z8j;u`(x&QQ&~f%vz;_#C!zs?5v*;#NZzRz&ES z0r~X1b$zP2%Z7P1zrB<8__)j5q)fIsv6Wbh!9^u;LAfYtMepR)UCfEc6#65Wsz<2| zNv*1c!(8!hJCMMIae1Qpse=(;Q<5D0k+2K<wXbK9Oser#p_S9M zEj(iQ5!Q}KFyoY|(+Du8|nin?b6)0?_W9w3+%fIF8O{;_YJG&KU zZHC6KXrogUwVw^hl<;Dk`9m!ed@&1IfiG{wSSKER027kbr-kDz!JNEuju_{w=EA0+ z27;lkeM^^ILSq?+9G7Ckbwq=SewlK-SkBFl6yJAVdMV(Kj2~hfAaSvBE%|eQBPE~G z<`ZpgI0d1;3~FPy|73X9iy>P zKZw`0KM4P0CJ`g9Pq1|FL?%$4Fw4RTXU3r?zbzt^-;i}TgobCj%4|^L250--P~UiP zU&;I2WJ#Waj8N{eJEWc|o>A?66uYItLP_8EK_})_qJ?>MSM3w{-9M*}752CJzeHqZ z+gDxDtdvbp^mSk5Y(yL5Vy1=FIxrQ@s}kiQ<1p5a%QD5DF%wX(CyYB}`UIta)av1K zW>L7`gWoN*nvx32ipmTvN&2K&QJR37s1$tTZB;m_R;Nt_u*ZB-!KfQ$1!z9+i~?q6 z2P8k8vEY7ofW~uyj052GL;{030IJW(J_o?&xuC-dDLk2eZ~`2j3u0UV$7fRm7r^4V zK*J5N{dX%Aa3=R39Ek@qBXG0F#}d-Vd$E&(?RWsl&kmp`!yq@rzz!bf0Z@@bDtO+j zzzm`iz6sgOMGP|k{PR=oHw#984NxV0npz-Q;{QsC|HFX%t@g_S`@IIJJV$!M|ItNb z`Y#C`>K+INpS%VLJU4@t7eEMDd-}{8&nk`sj^TwQ_tenO5W@>+nRtLWOT4~5Eclog zK=N$B;DZ?0lpqFnupZwZ(Dw-(Q2&P=PpEo~{>Hi6f|8I7){E+&3KSH{WD$W@WO<4}`0t@=TyWfD> z1t4ZLlRxJ8BX?LZNB}_eoPXw%?b6~uHWcvwKRA*g1fQ|}2mb*1_?%deal3pdD;s{2d+_%H93&}TM84H@hSVLyin6NZGjkAF7k zCcuM#{1fIcoxH!oh$j4D(||=p{)AB!frQb{0JFaV&@=ws@p5>**veLrLH7c3B>z6t z+0PFA;A;^8-gC>@vL~`6aiE|kh5qjWw*#}i0jNLs=TkEOjA5$(X7)6O@#~&5V_AU% z55D=6*~}YAW;*{Eu;Rn(Fu-4dfBNcQ*_;2HnpNYUP|rd*;Cvw9)pO;4tqA*XGvxx(U zpHXFT2wINzgff7^;sCN|wDAe;#rO{jj}5>Emxx0e_Egy=w?T9}017G!ay|3rugY97 z|HHSE08l+s|GMn?i<*G_M74r8#Rou^*^_{mi2$%*F-ZUebn(9k8aU$#ktX;b&Y>iL z1Db>SKS&uJ00As31>v~;ix7b;APB$}OfChW2WLtF;K8ko08}ucGyom8mkE+N)c*s& C96lWY From 1e9a9f3e6e7094ae126a6722ecc06a58335e865d Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Tue, 19 Sep 2017 10:51:32 +0300 Subject: [PATCH 28/50] updated the plugin of tree generator. --- ...ssets.jar => SimArboreal-1.2.1-assets.jar} | Bin 6569308 -> 6566730 bytes ...boreal-1.2.0.jar => SimArboreal-1.2.1.jar} | Bin 59435 -> 59435 bytes ...jar => ss-editor-tree-generator-1.0.4.jar} | Bin 84017 -> 84018 bytes 3 files changed, 0 insertions(+), 0 deletions(-) rename embedded-plugins/ss-editor-tree-generator/libs/{SimArboreal-1.2.0-assets.jar => SimArboreal-1.2.1-assets.jar} (99%) rename embedded-plugins/ss-editor-tree-generator/libs/{SimArboreal-1.2.0.jar => SimArboreal-1.2.1.jar} (93%) rename embedded-plugins/ss-editor-tree-generator/{ss-editor-tree-generator-1.0.3.jar => ss-editor-tree-generator-1.0.4.jar} (90%) diff --git a/embedded-plugins/ss-editor-tree-generator/libs/SimArboreal-1.2.0-assets.jar b/embedded-plugins/ss-editor-tree-generator/libs/SimArboreal-1.2.1-assets.jar similarity index 99% rename from embedded-plugins/ss-editor-tree-generator/libs/SimArboreal-1.2.0-assets.jar rename to embedded-plugins/ss-editor-tree-generator/libs/SimArboreal-1.2.1-assets.jar index 235e519e714b5f35260bed832ad7f07eb128f0ea..8132fd35a00270d9d90933b76be66b8ccecb5986 100644 GIT binary patch delta 3360 zcmZYBc|26>8vyV##=b9O-(@NLItD|S$eJY$W$&C&mK2J_gqWf$w46#*QX*Nau@p&V zEp#VK*JxKt5)!5S-Z|?2KKEYZ`#I0N&w1W?-uL`*-qCM)`0nvMypX3Wo|7MfczGeJ zo=LutCZ2sEy5Kj0ONJ)%tim8c zn{JSx1Ly*JfIeWrFi0@W=fdu2-)Bf~-)96E115keUN>w|Ueu=QBa1=FqPLV!?)_1xBHD0>uDJ_2j71IGFz@FYJL5mERP z2Ntb918?NSqSRveG)@BC=ep}#F83N92y#_|AO#3Q`^^zWbp3glKojNg>7isIR?X5o zj&S!oI!!v=o4-8sjFIek@hz_#aqLP#-L5OaQ@r+#)?*(T0I470P z;xF?C-wC+8|7dy&h3YTopu5i8yTEpmKFN&UaJBTHPb^{VGTBy0M15 zq{Mp>qMp!zQZn%^QzYWLcv^t<1ChZGc%SxTWdxOLJfWm8z0)?8!#wnI#*&)L^Dd7zin|8VXW@zjxUvuiUTWVK4fDkN&?2>6(GdVAeL z^EKc2h+380>!h9atT27#!6`^59~dtRc#iL;B8s&JEQ7fO6c8vl8s!R)D8@3fQs-tV5(vXt3U zH$LAN$Mn9uD)VL?$Wx=_!+#DB`4k%U>U;DP0yM7Z$)@wt_D(2W3IhL%mx9opwMSKR z4*r;v^yFhHT%y|BJLZ;-JRGpyq%B<;WaHD)sz!O=ey1k4C(qDt=J}$dBoe^`^L^KG)^y{E+w~&kHVJBi<6r+a&8OeRl+iJ#{Mn}w1i-I-6soj`5AKIB+vj53hNpk9;78TmYX_PuJ#b+ApH` zwHsYmdaY{L!yXl0f@>uW1=7fo6PwRHT-(jKDDlJqxEs@j9GI`?13%}cs!?yGwk+@X9?5F}_#Dv`f^Nj0kx5#{yidRL^ zi{wbw9wu}152Yd99@ee1;*~u&vbBvYEuQMUkK>vqjQ+)Zd(3P;KB>Bu=DxUeW6@_o z^m2*hth6o%qhXDHxLF|GaOxD@P~EVE8PhgDA=O$_kVGrJK%R#B->Yj02fwbN#B?Q` z(6qOa&{5u_p7=^#uf%+BPyVpAc$3B8=GwpHnz%;gA51OFGhp7QJaDKUtJdh$+7MkL zamaYNzc_Jlt3{DWP11X*5HazZa6!k;fnm}?{kK{=AkNxiKP(eOvlH zY-56~!$vZH2fZeUJvJg{`YYvUieN`f^q48)jHRBLBI~hWjTz#E1@XUwR8K*b!PU_qEU;>`E=X$3VRBa-Mtb3_+QE}0|p_}DTSt+qm> zP(#wMHMo%wd5*Rk^r0mpjUFH&qRQ+EmIUgCeen=f$%h?xpx=L`Y2j!!3G5VmGgzP$ zzt94s`F*e~s?!P!p^r!i_9#$83y>rAV!tMhUhRX0L_A$_90dHzIhON2$Hw1l=_;*Y3EVR!Ykwc3t5mBtM8g{MfYxbH}Y6)=% zpL2nwJl29B+23mc*V43`(<>|2vHDeOV+FdJS;QJwLsNeb=YwI6FdSNCg_vONnEseL zOaN0J`_Gh{xKSJ#(ZV{>A%oe@iy&xPGuWaDb3_y?_Bd9c^NJIK!h|76`XBvpN}~m2 z1Us9eP3&R?1q8)uBJz6w+CEof%|iWmybyF&6M{7UF^7vX^7kX*liOhujd;3pft`aO PP4H<2Pat760>1qZvr=`p delta 5906 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 M^Dvl-Gyo0zKYycf_W%F@ diff --git a/embedded-plugins/ss-editor-tree-generator/libs/SimArboreal-1.2.0.jar b/embedded-plugins/ss-editor-tree-generator/libs/SimArboreal-1.2.1.jar similarity index 93% rename from embedded-plugins/ss-editor-tree-generator/libs/SimArboreal-1.2.0.jar rename to embedded-plugins/ss-editor-tree-generator/libs/SimArboreal-1.2.1.jar index a2f60d4dea08f91bd0f6789213a5f58f696dacdb..8c158ab273327b5321784cfd072b6ea923888377 100644 GIT binary patch delta 641 zcmZ2|fqC@>X5IjAW)?061`ZB}a9!hxylTuK%FGK)H7kOt<=SBCstJhN%)^+@4rY{b z*l>awTLlZ4L5#^7qLyHR6w&o;V1}5?K~^y1mZAv{m?5Yq3lT9ie#a;X)a1SI;*Qi` zEDQ{(Kvx(~UT7!3+22$eq9)f$nFXwCvR%CZA4qYWG4sY32gTD zcb0`H%9+FnW@zLs1-obSg~Iu~U^@aN_(C|D7#LPDGceeK?3ldavEt?f^} z(@XEUgX#D862Wxv{a`S?@_sCs=6sL_ri&hgf$7r^;=r`d!%#5Y@-P)lzj+8T*Zom6 zn7{N<3Yg}34AEcuI0MXo@HiVx`#p&Q)2pAPgK5#H5c6`MLd-w+6k?zHvmmg1%`<;6 WeePK@m^OJ1v3JgMA29#*a|ZyHx7U^c delta 641 zcmZ2|fqC@>X5IjAW)?061`ZB}yg;RiylTuK%FK&77f3ZLf~n=&V8N>&yS zQO04z31(~+EMNvPCToaVf(24U*Rz2cVloF=!HipqCOlwi+ruV8`Xo;W6u*Eb*O+sOxJG|Zg^Q7gN^4Psr|-jCd1si>0`5LaG3yBlKf zKS&nl4=o`+& zbx93xFsv=OE`lJ2tcm4lZK?9qeMg2pgpG946hsEhydLxH#+cdu6q=5JpX6fq@Kg=l zf8?;s=Ti7HUAq8Vo9OJ8`LC-Fsit2GKTe$`pZSsM+BR^aBq3``|Lle$&B~_Nckj{- zw!W^V%BWf%c{Elu zxc46-HhggTMQePtxJLBV!r|OKqtxoIna1i(KFFrEN!}rGhp`s_P}qomyV_tjBfvv;r8SgcITzisgP*Rg8k|p6I_94m z8(FHEPAm;M)af3ry?m1XkaFt%>5Z)y;zsTFAOR>dV~m71|8?C7x_ebborF&BFM<0@ zb`Iw_j0;hYYKKWlkAEg*WMu?ogjF=RRbWn}GmGjL)vmtkuJSfNvJ{0#R8{Euj#hO= zFs}sIm!EollO(IKBf05z!S8FwsGVxw^Lw4O9`GzPwQP@R+?sWs8&(w558GXHe(o%z zEBt`;BZZ+9k&Fe?wq%L&%gRH7fd^ty{~vznb*fYujx18|Y_}3#@AFqmpHOCK-WTV` z?lBCFYB8w*y@=9Tsg1@Y!@74Vd4j$f7mwF5ADxM(hf$Iky-%>s?evpZ_nA@6Pc?)j zX86~uywPsoc4)x1)$X$&ZTTY&DnzmWw>2i9I+ZedpW5&9&q$4i%3({Fs?~yyU zkCso95f@26=rQ>5r4nAvJ1aBUy0c~zUN_MzVIs{Q|5AHa`o7H@7>9R8jO1OW{3Ne;dZ8*TJd^q>^XA~~ zxIY><)yz}k-2Tj#5eX|#H)+|9JGvu8y#C2>*p&3>jKZ18hhH9DE>IuRv~u+RAlY_9 z?+U%S$mFZfMCARiUcE!RWF(~?cln+jyc18(mr!{ZBz0J?EVw+iALo|U7Kw2dXIixS z3k#B0AGscCId@Pfy;y!KCTUX4jT-3IALyAdkQXtsKEl0cx)Xiy(7o?|Plae|ibKx` zCGc{bNx%4Pps755-Qcx6_cn#@j|VoBzJ9Wl$Is>bl8@9a?7@qasD_7<4?!re1WRykJ#Ylgks>xoCwMJE5qn* zaP8nyp}}uzLzcoFql&$A4!w~l+vQRs)3k|c4|9(;imV(sH}93{=19$-wwAX`F!<7q zpSh=fZa(*~l$zM?rA0JrRdA@_p#9nt{7hxu^s-2Du=M!SqNv51;m{o>^@3-d4Vf_L zh*^w&Arj>${p(V!w$OrOmSAmbkJ4~W?#+R*ojDkbd&hO8-+x@3FSt^Tt`gvR?K_Xt^ zXk@M zXP?h%^XD-;c@PB8M{`u)N>iC6(j3O|bz8sc!k)APE0(aZYQ-F$(ac#s&@052m`wyA z3Pa3$L~s>e6_`|UAj#3_57_d^fMVq$ii3R z-0&GIaReDLLJ-TplSqAv6RKtc+~n9Hw$5q?Kijw~f(Rokrk`)Z$y6q>Xs-!(vQZP( zWJXM`L8Rp{MbImzT+`N+W$tPIZnBP7w8|6^;P$kz&9x{0Fy&EYGj64U8OvOp;F{ae zif=|W+qm=PA60l%z#NFemNMo*61bp+=0I8TKf82k;AgiP?p2Na{aYcduU^j1?}hb~ zta_daKJ=O;Afaj&KpLt}7A*BDfvqZ{s0EONYBO8yByv;%F(8IAEWmoW&C6PT1-}6V z$>&87lYc!c1~s(=jxh9;a^*(~E5N}0H&leyTC(0=eE delta 3127 zcmZ9OcUV)|7RB$OhZ?HVCLkaPNC`!ngc3r@qe^)SSa?HkmW-exfsw8Q4goG*aYm#U zDM1iLqNt#V!=N-lKm}$5Q4uf>&Pz_BQ|>?K_pP<|zWbK%JLl?o{^s-iVjg5(K4AnA z5<-|2_+~L2;AO2eeUJdx2H?nETQ-0bKNRc*iBQNA)E0t3mq7U-c*^JrO0u$Kmy)&< zbO8j3kU|hAnx~+4OQ+(j0L)z2R?7pyRYO}4!b#n`oN}UJE(gX;#5k~M-p2tsZ!^*MRWTK*sv=%i{k|`a z?CetCJ?eT?NTlT1cX9TvZ`MC;`u~}_BshLoViTNZmM|%U%XWGx#s>*aT}GDkr$b*o zC>mRNO33-lcNh=`%u-5bkJgGfPZqr8@vft`0@dM zXFqN6PUW(I1);->5s~WD4j`_6U^W)A1OaJrge! zx>A>tkM*q}YYC5GQB_Q($3CjmvdEYmdQ7Sm8<3}7hxxekNTy4?N?tG~R^+2{+LZG! zrCP}+GOcSMWNLA-r>i?YC+5xitep_x|*>`-DdMZ*RIx6mm zk{PC3sG~{af@E{$UZGb1+~=XWA+`g%yG^HN%CdPBV(N3sm|LY?|F?Qu>8n7`WLBDwmfGZR{2qc0K0>~2DNwO8 zm$RFnuETudp(T&EzPn|=tq+wn5^y|qesa|J;d9^obNdsVqXXM?%dVT7V@LA>T>T1M zNP{Jsv~kLcljnu-@Kt=N!iI!abAI;+=>qGjkDIwQ!+DmIDpj|-q;3e$L?+;RPXwqG zWMzib71ahfpWwZ~yw=&ecll11`i$eOQ_-u4`$0k)V&m^p8Z5*$`)}4#cko(K@snOZ zTpuh;q#inxle#3Ag%j;MwYbOhfMv&rXuFXQt*_n{rd$Z4@5@TR;aEH|qb}R8$}A$` zwws+(8$F}x6u|&i&gksj@S~^p{v%kC@ zcPji|`Nb=0H)MNnW8N&vxKX8ZdzN`Dd2rVcV4ITA%tqeUQ&#truK2ICIwUg6ziahd zSfg3?U>`eRd#`EtFT3`~l-@y-V^U1FraU>9SS7i+|I(ZPrhB^+>{frmYFt-7{cZZ{ zyu#x+(r25e7B5aR2Z+?vg}e02)R^?u$E4Ly8P=`Y%O{zbOVsj(xrtw>I+j1<#1}Mq zPny5W=xE9=XIhk`ZCoLy`c1va5BDSa&oAXW@LmeNRU#h0zOI~6_tTp#lvU+~d*9@S zxcf}fRX!7>>}s;81ihdKbJIEGpMToFTpy$=yqGorhglGXVB6T>HQQiL$=$Bxa&WRD zURpfnV1Gr&jrTzZHv|aJ{gvLWnB>~4-*gL$eg4hVA6x#h^R~Rr+`zDR9NKG|9o%^g z^JJlbT5173beaG#w4^doXyud*aJJFFi-^oy}2dZ!eSh9UZp45)`^0(z1hP~>RkTYVqk_PslD14Xr1;fFA?{Gp=&cjN3=b97I7Y*_>OxX3n+3EV#Fr zumIjLgSX_4ap4PyT7htA#anUv%zpvj-5?ypKKj6YAHBC59D+$*Yt|c^%cT9Nh@;`M zWY7d_06!plmTgSYpm88i2z~U$m|`1hd0b;VYD3^wxD!~0Wf|LG{|mx8WWXcC`}N>P z-r6VY5ygWbJgl=15&7$CO-}w=WQ1eSXmDxxB1sNGMs_2J!(Tz9HQ60Cv;p2K>=0XL zb%U2WcSR5^vT3`~4u{j95kv=UxWkRxu!ggua}61q+cN>wz!tz?1a+}xndHvDnPTV_ zTObAB&~>)?dGc?jD!S8-+t$jCWxjvTHPzA5FGelf=wtq^N*u-518LY2XAcyCC;Fp3 z(AECeOR5?S^+GhAAh!PqP4l764ginp@dHV; zj>w(TlgJvTPKs@5qlHAE0;lL=tGzND6(b8|VFA3sy2+uO9)KX(ya;0Rj~g77?6642(k%vEwoz?{ck`Kv9CuCwi( rS{$WO3t&*PBTG5s)oRdNDYfm`~lp^^UsbI2>? From 70fafdbab28d988f26b824032d647d9f96f559a6 Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Tue, 19 Sep 2017 10:51:58 +0300 Subject: [PATCH 29/50] fixed some bugs --- .../java/com/jme3/util/SafeArrayList.java | 8 +- .../ss/editor/manager/FileIconManager.java | 17 ++-- .../java/com/ss/editor/util/EditorUtil.java | 87 +++++++++++-------- .../java/com/ss/editor/util/NodeUtils.java | 9 +- 4 files changed, 64 insertions(+), 57 deletions(-) diff --git a/src/main/java/com/jme3/util/SafeArrayList.java b/src/main/java/com/jme3/util/SafeArrayList.java index 6bee76e7..0085b20d 100644 --- a/src/main/java/com/jme3/util/SafeArrayList.java +++ b/src/main/java/com/jme3/util/SafeArrayList.java @@ -99,13 +99,7 @@ public SafeArrayList(final Class elementType, final int capacity) { public SafeArrayList(final Class elementType, final Collection collection) { this.elementType = elementType; - - if (collection instanceof SafeArrayList) { - this.buffer = Arrays.asList(((SafeArrayList) collection).getArray()); - } else { - this.buffer = new ArrayList<>(collection); - } - + this.buffer = new ArrayList<>(collection); this.size = buffer.size(); } diff --git a/src/main/java/com/ss/editor/manager/FileIconManager.java b/src/main/java/com/ss/editor/manager/FileIconManager.java index aeff8f69..555c3a3e 100644 --- a/src/main/java/com/ss/editor/manager/FileIconManager.java +++ b/src/main/java/com/ss/editor/manager/FileIconManager.java @@ -193,26 +193,27 @@ public void register(@NotNull final BiFunction iconFinder) final String extension = directory ? "folder" : FileUtils.getExtension(path); final Array> iconFinders = getIconFinders(); + String url = extensionToUrl.get(extension); + + if (url != null) { + return getImage(url, size); + } + if (!iconFinders.isEmpty()) { for (final BiFunction iconFinder : iconFinders) { + url = iconFinder.apply(path, extension); - final String url = iconFinder.apply(path, extension); final ClassLoader classLoader = iconFinder.getClass().getClassLoader(); - if (url == null || !EditorUtil.checkExists(url, classLoader)) { continue; } + extensionToUrl.put(extension, url); + return getImage(url, classLoader, size); } } - String url = extensionToUrl.get(extension); - - if (url != null) { - return getImage(url, size); - } - String contentType; if (directory) { diff --git a/src/main/java/com/ss/editor/util/EditorUtil.java b/src/main/java/com/ss/editor/util/EditorUtil.java index 4511641d..fe8ccf99 100644 --- a/src/main/java/com/ss/editor/util/EditorUtil.java +++ b/src/main/java/com/ss/editor/util/EditorUtil.java @@ -83,6 +83,7 @@ public abstract class EditorUtil { * @param paths the list of files. * @param content the content to store. */ + @FXThread public static void addCopiedFile(@NotNull final Array paths, @NotNull final ClipboardContent content) { final List files = paths.stream() @@ -121,22 +122,22 @@ public static void addCopiedFile(@NotNull final Array paths, @NotNull fina * @param path the path to resource. * @return true if the resource is exists. */ + @FromAnyThread public static boolean checkExists(@NotNull final String path) { final Class cs = EditorUtil.class; - return cs.getResourceAsStream(path) != null || - cs.getResourceAsStream("/" + path) != null; + return cs.getResource(path) != null || cs.getResource("/" + path) != null; } /** * Check exists boolean. * - * @param path the path to resource. + * @param path the path to resource. * @param classLoader the class loader. * @return true if the resource is exists. */ + @FromAnyThread public static boolean checkExists(@NotNull final String path, @NotNull final ClassLoader classLoader) { - return classLoader.getResourceAsStream(path) != null || - classLoader.getResourceAsStream("/" + path) != null; + return classLoader.getResource(path) != null || classLoader.getResource("/" + path) != null; } /** @@ -146,8 +147,8 @@ public static boolean checkExists(@NotNull final String path, @NotNull final Cla * @param classLoader the class loader. * @return the external form or null. */ - @Nullable - public static String toExternal(@NotNull final String path, @NotNull final ClassLoader classLoader) { + @FromAnyThread + public static @Nullable String toExternal(@NotNull final String path, @NotNull final ClassLoader classLoader) { if (!checkExists(path, classLoader)) return null; URL resource = classLoader.getResource(path); if (resource == null) resource = classLoader.getResource("/" + path); @@ -162,6 +163,7 @@ public static String toExternal(@NotNull final String path, @NotNull final Class * @param second the second point. * @return the angle between these points. */ + @FromAnyThread public static float getAngle(@NotNull final Vector2f center, @NotNull final Vector2f first, @NotNull final Vector2f second) { @@ -212,8 +214,8 @@ public static float getAngle(@NotNull final Vector2f center, @NotNull final Vect * * @return the user name. */ - @NotNull - public static String getUserName() { + @FromAnyThread + public static @NotNull String getUserName() { return System.getProperty("user.name"); } @@ -224,6 +226,7 @@ public static String getUserName() { * @param camera the camera of the screen. * @return true of we can see the position on the screen. */ + @FromAnyThread public static boolean isVisibleOnScreen(@NotNull final Vector3f position, @NotNull final Camera camera) { final int maxHeight = camera.getHeight(); @@ -245,8 +248,9 @@ public static boolean isVisibleOnScreen(@NotNull final Vector3f position, @NotNu * @param store the container of the result. * @param length the distance. */ + @FromAnyThread public static void movePoint(@NotNull final Vector3f first, @NotNull final Vector3f second, - final @NotNull Vector3f store, final int length) { + @NotNull final Vector3f store, final int length) { store.x = first.x + (second.x - first.x) * length; store.y = first.y + (second.y - first.y) * length; store.z = first.z + (second.z - first.z) * length; @@ -258,8 +262,8 @@ public static void movePoint(@NotNull final Vector3f first, @NotNull final Vecto * @param time the unix time. * @return the string presentation. */ - @NotNull - public static String timeFormat(final long time) { + @FromAnyThread + public static @NotNull String timeFormat(final long time) { final SimpleDateFormat format = LOCATE_DATE_FORMAT.get(); return format.format(new Date(time)); } @@ -271,8 +275,8 @@ public static String timeFormat(final long time) { * @param file the file. * @return the relative path. */ - @NotNull - public static Path getAssetFile(@NotNull final Path assetFolder, @NotNull final Path file) { + @FromAnyThread + public static @NotNull Path getAssetFile(@NotNull final Path assetFolder, @NotNull final Path file) { return assetFolder.relativize(file); } @@ -282,8 +286,8 @@ public static Path getAssetFile(@NotNull final Path assetFolder, @NotNull final * @param file the file. * @return the relative path. */ - @Nullable - public static Path getAssetFile(@NotNull final Path file) { + @FromAnyThread + public static @Nullable Path getAssetFile(@NotNull final Path file) { final EditorConfig editorConfig = EditorConfig.getInstance(); final Path currentAsset = editorConfig.getCurrentAsset(); if (currentAsset == null) return null; @@ -296,8 +300,8 @@ public static Path getAssetFile(@NotNull final Path file) { * @param assetFile the file. * @return the absolute path to the file. */ - @Nullable - public static Path getRealFile(@NotNull final Path assetFile) { + @FromAnyThread + public static @Nullable Path getRealFile(@NotNull final Path assetFile) { final EditorConfig editorConfig = EditorConfig.getInstance(); final Path currentAsset = editorConfig.getCurrentAsset(); if (currentAsset == null) return null; @@ -310,8 +314,8 @@ public static Path getRealFile(@NotNull final Path assetFile) { * @param assetFile the asset path to file. * @return the absolute path to the file. */ - @Nullable - public static Path getRealFile(@NotNull final String assetFile) { + @FromAnyThread + public static @Nullable Path getRealFile(@NotNull final String assetFile) { final EditorConfig editorConfig = EditorConfig.getInstance(); final Path currentAsset = editorConfig.getCurrentAsset(); if (currentAsset == null) return null; @@ -323,6 +327,7 @@ public static Path getRealFile(@NotNull final String assetFile) { * * @return true if you have a file in your system clipboard. */ + @FXThread public static boolean hasFileInClipboard() { final Clipboard clipboard = Clipboard.getSystemClipboard(); if (clipboard == null) return false; @@ -336,8 +341,8 @@ public static boolean hasFileInClipboard() { * @param path the path * @return the valid asset path for the file. */ - @NotNull - public static String toAssetPath(@NotNull final Path path) { + @FromAnyThread + public static @NotNull String toAssetPath(@NotNull final Path path) { if (File.separatorChar == '/') return path.toString(); return path.toString().replace("\\", "/"); } @@ -349,6 +354,7 @@ public static String toAssetPath(@NotNull final Path path) { * @param owner the owner * @param e the e */ + @FromAnyThread public static void handleException(@Nullable final Logger logger, @Nullable final Object owner, @NotNull final Exception e) { handleException(logger, owner, e, null); @@ -362,6 +368,7 @@ public static void handleException(@Nullable final Logger logger, @Nullable fina * @param e the e * @param callback the callback */ + @FromAnyThread public static void handleException(@Nullable Logger logger, @Nullable final Object owner, @NotNull final Exception e, @Nullable final Runnable callback) { if (logger == null) { @@ -425,8 +432,8 @@ public static String buildStackTrace(@NotNull final Exception exception) { /** * Create a dialog for showing the exception. */ - @NotNull - private static Alert createErrorAlert(@NotNull final Exception e, @Nullable final String localizedMessage, + @FXThread + private static @NotNull Alert createErrorAlert(@NotNull final Exception e, @Nullable final String localizedMessage, @Nullable final String stackTrace) { final TextArea textArea = new TextArea(stackTrace); @@ -459,6 +466,7 @@ private static Alert createErrorAlert(@NotNull final Exception e, @Nullable fina * * @param path the path */ + @FromAnyThread public static void openFileInExternalEditor(@NotNull final Path path) { final Platform platform = JmeSystem.getPlatform(); @@ -501,6 +509,7 @@ public static void openFileInExternalEditor(@NotNull final Path path) { * * @param path the path */ + @FromAnyThread public static void openFileInSystemExplorer(@NotNull Path path) { final Platform platform = JmeSystem.getPlatform(); @@ -546,6 +555,7 @@ public static void openFileInSystemExplorer(@NotNull Path path) { } } + @FromAnyThread private static boolean isAppExists(@NotNull final String command) { final Runtime runtime = Runtime.getRuntime(); @@ -564,10 +574,10 @@ private static boolean isAppExists(@NotNull final String command) { * Convert the object to byte array. * * @param object the object - * @return the byte [ ] + * @return the byte array. */ - @NotNull - public static byte[] serialize(@NotNull final Serializable object) { + @FromAnyThread + public static @NotNull byte[] serialize(@NotNull final Serializable object) { final ByteArrayOutputStream bout = new ByteArrayOutputStream(); @@ -584,11 +594,11 @@ public static byte[] serialize(@NotNull final Serializable object) { * Convert the byte array to object. * * @param the type parameter - * @param bytes the bytes - * @return the t + * @param bytes the byte array. + * @return the result object. */ - @NotNull - public static T deserialize(@NotNull final byte[] bytes) { + @FromAnyThread + public static @NotNull T deserialize(@NotNull final byte[] bytes) { final ByteArrayInputStream bin = new ByteArrayInputStream(bytes); @@ -606,6 +616,7 @@ public static T deserialize(@NotNull final byte[] bytes) { * @param mod the mod * @return the float */ + @FromAnyThread public static float clipNumber(float value, float mod) { return (int) (value * mod) / mod; } @@ -637,8 +648,8 @@ public static void decrementLoading() { * @param value the enum value. * @return the array of enum values. */ - @NotNull - public static > E[] getAvailableValues(@NotNull final E value) { + @FromAnyThread + public static > @NotNull E[] getAvailableValues(@NotNull final E value) { final Class valueClass = value.getClass(); if (!valueClass.isEnum()) throw new RuntimeException("The class " + valueClass + " isn't enum."); final Enum[] enumConstants = valueClass.getEnumConstants(); @@ -654,9 +665,9 @@ public static > E[] getAvailableValues(@NotNull final E value) * @param resultType the result type. * @return the new instance or null. */ - @Nullable - public static T tryToCreateUserObject(@NotNull final Object owner, @NotNull final String className, - @NotNull final Class resultType) { + @FromAnyThread + public static @Nullable T tryToCreateUserObject(@NotNull final Object owner, @NotNull final String className, + @NotNull final Class resultType) { final ResourceManager resourceManager = ResourceManager.getInstance(); final ClasspathManager classpathManager = ClasspathManager.getInstance(); @@ -697,8 +708,8 @@ public static T tryToCreateUserObject(@NotNull final Object owner, @NotNull * @param consumer the change consumer. * @return the default layer or null. */ - @Nullable - public static SceneLayer getDefaultLayer(@NotNull final ChangeConsumer consumer) { + @FromAnyThread + public static @Nullable SceneLayer getDefaultLayer(@NotNull final ChangeConsumer consumer) { if (!(consumer instanceof SceneChangeConsumer)) { return null; diff --git a/src/main/java/com/ss/editor/util/NodeUtils.java b/src/main/java/com/ss/editor/util/NodeUtils.java index 2876b744..7040a782 100644 --- a/src/main/java/com/ss/editor/util/NodeUtils.java +++ b/src/main/java/com/ss/editor/util/NodeUtils.java @@ -9,10 +9,10 @@ import com.jme3.scene.Geometry; import com.jme3.scene.Node; import com.jme3.scene.Spatial; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import com.ss.rlib.util.StringUtils; import com.ss.rlib.util.array.Array; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.function.Consumer; import java.util.function.Predicate; @@ -236,8 +236,9 @@ public static void visitSpatial(@NotNull final Spatial spati if (type.isInstance(spatial)) { consumer.accept(type.cast(spatial)); - return; - } else if (!(spatial instanceof Node)) { + } + + if (!(spatial instanceof Node)) { return; } From 27221791af14d9ae80caf4ebb56ecfccd26c664c Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Tue, 19 Sep 2017 15:12:25 +0300 Subject: [PATCH 30/50] updated tonegod.emitter version. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 99605877..81befeca 100644 --- a/build.gradle +++ b/build.gradle @@ -128,7 +128,7 @@ dependencies { compile ('com.github.JavaSaBr:jme3-spaceshift-extension:1.6.0') { exclude group: 'org.jmonkeyengine' } - compile ('com.github.JavaSaBr:tonegodemitter:develop-SNAPSHOT') { + compile ('com.github.JavaSaBr:tonegodemitter:2.4.0') { exclude group: 'org.jmonkeyengine' } From 37a352f80823f0fae069f9df9b9a6a71f9fc8dfa Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Wed, 20 Sep 2017 07:00:24 +0300 Subject: [PATCH 31/50] updated base file editor API. --- .../plugin/api/editor/Base3DFileEditor.java | 71 +---------------- .../plugin/api/editor/BaseFileEditor.java | 77 ++++++++++++++++++- 2 files changed, 77 insertions(+), 71 deletions(-) diff --git a/src/main/java/com/ss/editor/plugin/api/editor/Base3DFileEditor.java b/src/main/java/com/ss/editor/plugin/api/editor/Base3DFileEditor.java index 2ab0c995..8ee35f04 100644 --- a/src/main/java/com/ss/editor/plugin/api/editor/Base3DFileEditor.java +++ b/src/main/java/com/ss/editor/plugin/api/editor/Base3DFileEditor.java @@ -3,10 +3,6 @@ import com.jme3.math.Vector3f; import com.ss.editor.annotation.FXThread; import com.ss.editor.annotation.FromAnyThread; -import com.ss.editor.model.undo.EditorOperation; -import com.ss.editor.model.undo.EditorOperationControl; -import com.ss.editor.model.undo.UndoableEditor; -import com.ss.editor.model.undo.editor.ChangeConsumer; import com.ss.editor.state.editor.impl.AbstractEditor3DState; import com.ss.editor.ui.component.editor.state.impl.Editor3DEditorState; import javafx.event.Event; @@ -17,7 +13,6 @@ import org.jetbrains.annotations.NotNull; import java.nio.file.Path; -import java.util.concurrent.atomic.AtomicInteger; /** * The base implementation of {@link com.ss.editor.ui.component.editor.FileEditor} with 3D scene. @@ -25,13 +20,7 @@ * @author JavaSaBr */ public abstract class Base3DFileEditor extends - BaseFileEditor implements UndoableEditor, ChangeConsumer { - - /** - * The operation control. - */ - @NotNull - private final EditorOperationControl operationControl; + BaseFileEditor { /** * The 3D part of this editor. @@ -39,16 +28,9 @@ public abstract class Base3DFileEditor extends AbstractFileEditor { +public abstract class BaseFileEditor extends AbstractFileEditor implements + UndoableEditor, ChangeConsumer { + + /** + * The operation control. + */ + @NotNull + private final EditorOperationControl operationControl; + + /** + * The changes counter. + */ + @NotNull + private final AtomicInteger changeCounter; + /** * The state of this editor. */ @@ -34,6 +53,62 @@ public abstract class BaseFileEditor extends AbstractFile */ private boolean ignoreListeners; + protected BaseFileEditor() { + this.operationControl = createOperationControl(); + this.changeCounter = new AtomicInteger(); + } + + /** + * Create an editor operation control. + * + * @return the editor operation control. + */ + protected @NotNull EditorOperationControl createOperationControl() { + return new EditorOperationControl(this); + } + + @Override + @FromAnyThread + public void execute(@NotNull final EditorOperation operation) { + operationControl.execute(operation); + } + + @Override + @FXThread + public void incrementChange() { + final int result = changeCounter.incrementAndGet(); + setDirty(result != 0); + } + + @Override + @FXThread + public void decrementChange() { + final int result = changeCounter.decrementAndGet(); + setDirty(result != 0); + } + + @Override + @FromAnyThread + public void redo() { + operationControl.redo(); + } + + @Override + @FromAnyThread + public void undo() { + operationControl.undo(); + } + + /** + * Get the editor operation control. + * + * @return the editor operation control. + */ + @FromAnyThread + protected @NotNull EditorOperationControl getOperationControl() { + return operationControl; + } + @Override @FXThread public void openFile(@NotNull final Path file) { From f360633f3a65d75bf1bee48991b78a18d277610e Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Wed, 20 Sep 2017 08:47:30 +0300 Subject: [PATCH 32/50] refactoring --- .../editor/extension/loader/SceneLoader.java | 134 ++++++++++++++++++ .../material/BaseMaterialFileEditor.java | 5 +- .../layer/node/LayersRootTreeNode.java | 37 +++-- .../layer/node/SceneLayerTreeNode.java | 15 ++ .../tree/node/ColorsSettingsTreeNode.java | 2 + .../tree/node/OtherSettingsTreeNode.java | 2 + .../tree/node/RenderSettingsTreeNode.java | 2 + .../node/RootMaterialSettingsTreeNode.java | 5 + .../tree/node/TexturesSettingsTreeNode.java | 2 + .../ui/control/model/node/BufferTreeNode.java | 14 +- .../control/model/node/PositionTreeNode.java | 18 ++- .../model/node/VertexBufferTreeNode.java | 25 ++-- .../model/node/control/ControlTreeNode.java | 19 ++- .../node/control/SkeletonControlTreeNode.java | 16 +-- .../anim/AnimationControlTreeNode.java | 28 ++-- .../control/anim/AnimationTrackTreeNode.java | 21 ++- .../node/control/anim/AnimationTreeNode.java | 39 ++--- .../control/motion/MotionEventTreeNode.java | 25 ++-- .../control/motion/MotionPathTreeNode.java | 21 ++- .../physics/CharacterControlTreeNode.java | 16 +-- .../physics/RagdollControlTreeNode.java | 16 +-- .../physics/RigidBodyControlTreeNode.java | 17 +-- .../vehicle/VehicleControlTreeNode.java | 17 +-- .../physics/vehicle/VehicleWheelTreeNode.java | 17 +-- .../node/light/AmbientLightTreeNode.java | 16 +-- .../node/light/DirectionalLightTreeNode.java | 22 ++- .../model/node/light/LightProbeTreeNode.java | 18 +-- .../model/node/light/LightTreeNode.java | 27 ++-- .../model/node/light/PointLightTreeNode.java | 22 ++- .../model/node/light/SpotLightTreeNode.java | 22 ++- .../shape/BoxCollisionShapeTreeNode.java | 11 +- .../shape/CapsuleCollisionShapeTreeNode.java | 16 +-- .../shape/ChildCollisionShapeTreeNode.java | 26 ++-- .../physics/shape/CollisionShapeTreeNode.java | 16 +-- .../shape/ComputedCollisionShapeTreeNode.java | 21 ++- .../shape/ConeCollisionShapeTreeNode.java | 16 +-- .../shape/CylinderCollisionShapeTreeNode.java | 16 +-- .../shape/GImpactCollisionShapeTreeNode.java | 16 +-- .../HeightFieldCollisionShapeTreeNode.java | 16 +-- .../shape/HullCollisionShapeTreeNode.java | 16 +-- .../shape/MeshCollisionShapeTreeNode.java | 16 +-- .../shape/PlaneCollisionShapeTreeNode.java | 16 +-- .../shape/SphereCollisionShapeTreeNode.java | 16 +-- .../model/node/spatial/MaterialTreeNode.java | 7 + .../model/node/spatial/MeshTreeNode.java | 22 ++- .../model/node/spatial/SpatialTreeNode.java | 31 ++-- .../DefaultParticleInfluencerTreeNode.java | 11 +- .../EmptyParticleInfluencerTreeNode.java | 11 +- .../ParticleInfluencerTreeNode.java | 16 +-- .../RadialParticleInfluencerTreeNode.java | 11 +- .../shape/EmitterBoxShapeTreeNode.java | 16 +-- .../EmitterMeshConvexHullShapeTreeNode.java | 11 +- .../shape/EmitterMeshFaceShapeTreeNode.java | 11 +- .../shape/EmitterMeshVertexShapeTreeNode.java | 11 +- .../shape/EmitterPointShapeTreeNode.java | 16 +-- .../emitter/shape/EmitterShapeTreeNode.java | 16 +-- .../shape/EmitterSphereShapeTreeNode.java | 16 +-- .../Toneg0DParticleInfluencerTreeNode.java | 23 ++- .../Toneg0DParticleInfluencersTreeNode.java | 23 ++- .../editor/ui/control/tree/node/TreeNode.java | 40 +++++- 60 files changed, 589 insertions(+), 561 deletions(-) create mode 100644 src/main/java/com/ss/editor/extension/loader/SceneLoader.java diff --git a/src/main/java/com/ss/editor/extension/loader/SceneLoader.java b/src/main/java/com/ss/editor/extension/loader/SceneLoader.java new file mode 100644 index 00000000..1464cc7c --- /dev/null +++ b/src/main/java/com/ss/editor/extension/loader/SceneLoader.java @@ -0,0 +1,134 @@ +package com.ss.editor.extension.loader; + +import static java.util.Objects.requireNonNull; +import com.jme3.app.Application; +import com.jme3.app.state.AppStateManager; +import com.jme3.asset.AssetInfo; +import com.jme3.asset.AssetManager; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeImporter; +import com.jme3.export.Savable; +import com.jme3.export.binary.BinaryImporter; +import com.jme3.post.FilterPostProcessor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.util.ArrayDeque; +import java.util.Deque; + +/** + * The implementation of jME Importer to load scenes. + * + * @author JavaSaBr + */ +public class SceneLoader implements JmeImporter { + + /** + * The application. + */ + @Nullable + private static Application application; + + /** + * The filter post processor. + */ + @Nullable + private static FilterPostProcessor processor; + + /** + * Install a scene loader to the asset manager. + * + * @param application the application. + */ + public static void install(@NotNull final Application application) { + install(application, null); + } + + /** + * Install a scene loader to the asset manager. + * + * @param application the application. + * @param processor the processor. + */ + public static void install(@NotNull final Application application, @Nullable final FilterPostProcessor processor) { + final AssetManager assetManager = application.getAssetManager(); + assetManager.unregisterLoader(BinaryImporter.class); + assetManager.registerLoader(SceneLoader.class, "j3o", "j3f", "j3s"); + SceneLoader.application = application; + SceneLoader.processor = processor; + } + + public static @NotNull AssetManager tryToGetAssetManager() { + return requireNonNull(application).getAssetManager(); + } + + public static @NotNull AppStateManager tryToGetStateManager() { + return requireNonNull(application).getStateManager(); + } + + public static @Nullable FilterPostProcessor tryToGetPostProcessor() { + return processor; + } + + /** + * The thread local importers. + */ + @NotNull + private final ThreadLocal> threadLocalImporters; + + /** + * The current importer. + */ + @NotNull + private final ThreadLocal currentImporter; + + public SceneLoader() { + currentImporter = new ThreadLocal<>(); + threadLocalImporters = new ThreadLocal>() { + + @Override + protected Deque initialValue() { + return new ArrayDeque<>(); + } + }; + } + + @Override + public InputCapsule getCapsule(final Savable id) { + final BinaryImporter importer = currentImporter.get(); + return importer.getCapsule(id); + } + + @Override + public AssetManager getAssetManager() { + final BinaryImporter importer = currentImporter.get(); + return importer.getAssetManager(); + } + + @Override + public int getFormatVersion() { + final BinaryImporter importer = currentImporter.get(); + return importer.getFormatVersion(); + } + + @Override + public Object load(@NotNull final AssetInfo assetInfo) throws IOException { + + final Deque importers = threadLocalImporters.get(); + BinaryImporter importer = importers.pollLast(); + + if (importer == null) { + importer = new BinaryImporter(); + } + + final BinaryImporter prev = currentImporter.get(); + currentImporter.set(importer); + try { + return importer.load(assetInfo); + } finally { + importers.addLast(importer); + currentImporter.set(prev); + } + } +} diff --git a/src/main/java/com/ss/editor/plugin/api/editor/material/BaseMaterialFileEditor.java b/src/main/java/com/ss/editor/plugin/api/editor/material/BaseMaterialFileEditor.java index fc09c8b7..c3739712 100644 --- a/src/main/java/com/ss/editor/plugin/api/editor/material/BaseMaterialFileEditor.java +++ b/src/main/java/com/ss/editor/plugin/api/editor/material/BaseMaterialFileEditor.java @@ -104,6 +104,7 @@ protected BaseMaterialFileEditor() { * * @return the change consumer. */ + @FromAnyThread protected C getChangeConsumer() { return unsafeCast(this); } @@ -159,6 +160,7 @@ protected void createToolComponents(@NotNull final EditorToolComponent container * * @return the settings tree tool name. */ + @FromAnyThread protected @NotNull String getSettingsTreeToolName() { return Messages.MATERIAL_SETTINGS_MAIN; } @@ -184,6 +186,7 @@ protected void createToolComponents(@NotNull final EditorToolComponent container * * @param object the selected object. */ + @FXThread private void selectedFromTree(@Nullable final Object object) { Object parent = null; @@ -222,8 +225,8 @@ protected void loadState() { getLightButton().setSelected(editorState.isLightEnable()); } - @FXThread @Override + @FXThread protected @Nullable Supplier getEditorStateFactory() { return EditorMaterialEditorState::new; } diff --git a/src/main/java/com/ss/editor/ui/control/layer/node/LayersRootTreeNode.java b/src/main/java/com/ss/editor/ui/control/layer/node/LayersRootTreeNode.java index 3ba7a81c..9d3c1b4c 100644 --- a/src/main/java/com/ss/editor/ui/control/layer/node/LayersRootTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/layer/node/LayersRootTreeNode.java @@ -1,5 +1,9 @@ package com.ss.editor.ui.control.layer.node; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; +import com.ss.editor.extension.scene.SceneLayer; +import com.ss.editor.extension.scene.SceneNode; import com.ss.editor.model.undo.editor.SceneChangeConsumer; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.layer.LayerNodeTree; @@ -7,17 +11,13 @@ import com.ss.editor.ui.control.model.tree.action.scene.CreateSceneLayerAction; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; -import com.ss.editor.extension.scene.SceneLayer; -import com.ss.editor.extension.scene.SceneNode; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - +import com.ss.rlib.util.array.Array; +import com.ss.rlib.util.array.ArrayFactory; import javafx.collections.ObservableList; import javafx.scene.control.MenuItem; import javafx.scene.image.Image; -import com.ss.rlib.util.array.Array; -import com.ss.rlib.util.array.ArrayFactory; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.List; @@ -28,39 +28,35 @@ */ public class LayersRootTreeNode extends TreeNode { - /** - * Instantiates a new Layers root model node. - * - * @param element the element - * @param objectId the object id - */ public LayersRootTreeNode(@NotNull final LayersRoot element, final long objectId) { super(element, objectId); } - @Nullable @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { return Icons.SCENE_16; } @Override + @FXThread public void fillContextMenu(@NotNull final NodeTree nodeTree, @NotNull final ObservableList items) { items.add(new CreateSceneLayerAction(nodeTree, this)); } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { final LayersRoot element = getElement(); final SceneChangeConsumer changeConsumer = element.getChangeConsumer(); final SceneNode sceneNode = changeConsumer.getCurrentModel(); return sceneNode.getName(); } - @NotNull + @Override - public Array> getChildren(@NotNull final NodeTree nodeTree) { + @FXThread + public @NotNull Array> getChildren(@NotNull final NodeTree nodeTree) { final LayersRoot element = getElement(); final SceneChangeConsumer changeConsumer = element.getChangeConsumer(); @@ -74,6 +70,7 @@ public Array> getChildren(@NotNull final NodeTree nodeTree) { } @Override + @FXThread public boolean hasChildren(@NotNull final NodeTree nodeTree) { return nodeTree instanceof LayerNodeTree; } diff --git a/src/main/java/com/ss/editor/ui/control/layer/node/SceneLayerTreeNode.java b/src/main/java/com/ss/editor/ui/control/layer/node/SceneLayerTreeNode.java index 41abc22c..bcb3a7c7 100644 --- a/src/main/java/com/ss/editor/ui/control/layer/node/SceneLayerTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/layer/node/SceneLayerTreeNode.java @@ -2,6 +2,8 @@ import static com.ss.rlib.util.ObjectUtils.notNull; import com.jme3.scene.Spatial; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.extension.scene.SceneLayer; import com.ss.editor.model.undo.editor.ChangeConsumer; import com.ss.editor.model.undo.editor.ModelChangeConsumer; @@ -36,6 +38,7 @@ public SceneLayerTreeNode(@NotNull final SceneLayer element, final long objectId } @Override + @FXThread public void fillContextMenu(@NotNull final NodeTree nodeTree, @NotNull final ObservableList items) { super.fillContextMenu(nodeTree, items); @@ -48,6 +51,7 @@ public void fillContextMenu(@NotNull final NodeTree nodeTree, @NotNull final } @Override + @FXThread public void changeName(@NotNull final NodeTree nodeTree, @NotNull final String newName) { final SceneLayer element = getElement(); @@ -57,11 +61,13 @@ public void changeName(@NotNull final NodeTree nodeTree, @NotNull final Strin } @Override + @FXThread public boolean hasChildren(@NotNull final NodeTree nodeTree) { return true; } @Override + @FXThread public @NotNull Array> getChildren(@NotNull final NodeTree nodeTree) { final SceneLayer element = getElement(); @@ -81,12 +87,14 @@ public boolean hasChildren(@NotNull final NodeTree nodeTree) { } @Override + @FXThread public boolean canAccept(@NotNull final TreeNode child, final boolean isCopy) { final Object element = child.getElement(); return element instanceof Spatial && SceneLayer.getLayer((Spatial) element) != getElement(); } @Override + @FXThread public void accept(@NotNull final ChangeConsumer changeConsumer, @NotNull final Object object, final boolean isCopy) { @@ -109,27 +117,32 @@ public void accept(@NotNull final ChangeConsumer changeConsumer, @NotNull final } @Override + @FromAnyThread public @NotNull String getName() { final String name = getElement().getName(); return name == null ? "name is null" : name; } @Override + @FXThread public boolean canEditName() { return !getElement().isBuiltIn(); } @Override + @FXThread public boolean canMove() { return false; } @Override + @FXThread public boolean canCopy() { return false; } @Override + @FXThread public @Nullable Image getIcon() { return Icons.LAYERS_16; } @@ -140,12 +153,14 @@ public boolean isHided() { } @Override + @FXThread public void show(@NotNull final NodeTree nodeTree) { final ChangeConsumer changeConsumer = notNull(nodeTree.getChangeConsumer()); changeConsumer.execute(new ChangeVisibleSceneLayerOperation(getElement(), true)); } @Override + @FXThread public void hide(@NotNull final NodeTree nodeTree) { final ChangeConsumer consumer = notNull(nodeTree.getChangeConsumer()); consumer.execute(new ChangeVisibleSceneLayerOperation(getElement(), false)); diff --git a/src/main/java/com/ss/editor/ui/control/material/tree/node/ColorsSettingsTreeNode.java b/src/main/java/com/ss/editor/ui/control/material/tree/node/ColorsSettingsTreeNode.java index 507e519d..deec8669 100644 --- a/src/main/java/com/ss/editor/ui/control/material/tree/node/ColorsSettingsTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/material/tree/node/ColorsSettingsTreeNode.java @@ -1,6 +1,7 @@ package com.ss.editor.ui.control.material.tree.node; import com.ss.editor.Messages; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.model.node.material.ColorsSettings; import org.jetbrains.annotations.NotNull; @@ -16,6 +17,7 @@ public ColorsSettingsTreeNode(@NotNull final ColorsSettings element, final long } @Override + @FromAnyThread public @NotNull String getName() { return Messages.MATERIAL_SETTINGS_COLORS; } diff --git a/src/main/java/com/ss/editor/ui/control/material/tree/node/OtherSettingsTreeNode.java b/src/main/java/com/ss/editor/ui/control/material/tree/node/OtherSettingsTreeNode.java index 2ce14cd6..2182499e 100644 --- a/src/main/java/com/ss/editor/ui/control/material/tree/node/OtherSettingsTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/material/tree/node/OtherSettingsTreeNode.java @@ -1,6 +1,7 @@ package com.ss.editor.ui.control.material.tree.node; import com.ss.editor.Messages; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.model.node.material.OtherSettings; import org.jetbrains.annotations.NotNull; @@ -16,6 +17,7 @@ public OtherSettingsTreeNode(@NotNull final OtherSettings element, final long ob } @Override + @FromAnyThread public @NotNull String getName() { return Messages.MATERIAL_SETTINGS_OTHER; } diff --git a/src/main/java/com/ss/editor/ui/control/material/tree/node/RenderSettingsTreeNode.java b/src/main/java/com/ss/editor/ui/control/material/tree/node/RenderSettingsTreeNode.java index e9c118b0..2e57b888 100644 --- a/src/main/java/com/ss/editor/ui/control/material/tree/node/RenderSettingsTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/material/tree/node/RenderSettingsTreeNode.java @@ -1,6 +1,7 @@ package com.ss.editor.ui.control.material.tree.node; import com.ss.editor.Messages; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.model.node.material.RenderSettings; import org.jetbrains.annotations.NotNull; @@ -16,6 +17,7 @@ public RenderSettingsTreeNode(@NotNull final RenderSettings element, final long } @Override + @FromAnyThread public @NotNull String getName() { return Messages.MATERIAL_SETTINGS_RENDER; } diff --git a/src/main/java/com/ss/editor/ui/control/material/tree/node/RootMaterialSettingsTreeNode.java b/src/main/java/com/ss/editor/ui/control/material/tree/node/RootMaterialSettingsTreeNode.java index d7336705..aad400ba 100644 --- a/src/main/java/com/ss/editor/ui/control/material/tree/node/RootMaterialSettingsTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/material/tree/node/RootMaterialSettingsTreeNode.java @@ -1,6 +1,8 @@ package com.ss.editor.ui.control.material.tree.node; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.model.node.material.*; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; @@ -20,6 +22,7 @@ public RootMaterialSettingsTreeNode(@NotNull final RootMaterialSettings element, } @Override + @FXThread public @NotNull Array> getChildren(@NotNull final NodeTree nodeTree) { final RootMaterialSettings settings = getElement(); @@ -34,11 +37,13 @@ public RootMaterialSettingsTreeNode(@NotNull final RootMaterialSettings element, } @Override + @FromAnyThread public @NotNull String getName() { return Messages.MATERIAL_SETTINGS_MAIN; } @Override + @FXThread public boolean hasChildren(@NotNull final NodeTree nodeTree) { return true; } diff --git a/src/main/java/com/ss/editor/ui/control/material/tree/node/TexturesSettingsTreeNode.java b/src/main/java/com/ss/editor/ui/control/material/tree/node/TexturesSettingsTreeNode.java index e0eab9a0..a8afbfa9 100644 --- a/src/main/java/com/ss/editor/ui/control/material/tree/node/TexturesSettingsTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/material/tree/node/TexturesSettingsTreeNode.java @@ -1,6 +1,7 @@ package com.ss.editor.ui.control.material.tree.node; import com.ss.editor.Messages; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.model.node.material.TexturesSettings; import org.jetbrains.annotations.NotNull; @@ -16,6 +17,7 @@ public TexturesSettingsTreeNode(@NotNull final TexturesSettings element, final l } @Override + @FromAnyThread public @NotNull String getName() { return Messages.MATERIAL_SETTINGS_TEXTURES; } diff --git a/src/main/java/com/ss/editor/ui/control/model/node/BufferTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/BufferTreeNode.java index f599ac41..3c9804ec 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/BufferTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/BufferTreeNode.java @@ -1,5 +1,6 @@ package com.ss.editor.ui.control.model.node; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.tree.node.TreeNode; import javafx.scene.image.Image; @@ -15,25 +16,18 @@ */ public class BufferTreeNode extends TreeNode { - /** - * Instantiates a new Buffer model node. - * - * @param element the element - * @param objectId the object id - */ public BufferTreeNode(@NotNull final Buffer element, final long objectId) { super(element, objectId); } - @Nullable @Override - public Image getIcon() { + public @Nullable Image getIcon() { return Icons.DATA_16; } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { return getElement().getClass().getSimpleName(); } } diff --git a/src/main/java/com/ss/editor/ui/control/model/node/PositionTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/PositionTreeNode.java index 6ea8d8d6..a13482cf 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/PositionTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/PositionTreeNode.java @@ -1,6 +1,8 @@ package com.ss.editor.ui.control.model.node; import com.jme3.math.Vector3f; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.tree.node.TreeNode; import javafx.scene.image.Image; @@ -20,34 +22,30 @@ public class PositionTreeNode extends TreeNode { @Nullable private String name; - /** - * Instantiates a new Position model node. - * - * @param element the element - * @param objectId the object id - */ public PositionTreeNode(@NotNull final Vector3f element, final long objectId) { super(element, objectId); } - @Nullable @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { return Icons.WAY_POINT_16; } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { return name == null ? "Point 3D" : name; } @Override + @FXThread public boolean isNeedToSaveName() { return true; } @Override + @FXThread public void setName(@Nullable final String name) { this.name = name; } diff --git a/src/main/java/com/ss/editor/ui/control/model/node/VertexBufferTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/VertexBufferTreeNode.java index 85e534d2..c00ebf01 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/VertexBufferTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/VertexBufferTreeNode.java @@ -2,15 +2,17 @@ import com.jme3.scene.VertexBuffer; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.model.tree.ModelNodeTree; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; +import com.ss.rlib.util.array.Array; +import com.ss.rlib.util.array.ArrayFactory; import javafx.scene.image.Image; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import com.ss.rlib.util.array.Array; -import com.ss.rlib.util.array.ArrayFactory; import java.nio.Buffer; @@ -21,36 +23,31 @@ */ public class VertexBufferTreeNode extends TreeNode { - /** - * Instantiates a new Vertex buffer model node. - * - * @param element the element - * @param objectId the object id - */ public VertexBufferTreeNode(@NotNull final VertexBuffer element, final long objectId) { super(element, objectId); } - @Nullable @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { return Icons.VERTEX_16; } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { return Messages.MODEL_FILE_EDITOR_NODE_VERTEX_BUFFER + " [" + getElement().getBufferType() + "]"; } @Override + @FXThread public boolean hasChildren(@NotNull final NodeTree nodeTree) { return nodeTree instanceof ModelNodeTree; } - @NotNull @Override - public Array> getChildren(@NotNull final NodeTree nodeTree) { + @FXThread + public @NotNull Array> getChildren(@NotNull final NodeTree nodeTree) { final VertexBuffer vertexBuffer = getElement(); diff --git a/src/main/java/com/ss/editor/ui/control/model/node/control/ControlTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/control/ControlTreeNode.java index 7641086b..c3c66c3e 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/control/ControlTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/control/ControlTreeNode.java @@ -1,6 +1,8 @@ package com.ss.editor.ui.control.model.node.control; import com.jme3.scene.control.Control; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.extension.Named; import com.ss.editor.extension.scene.control.EditableControl; import com.ss.editor.ui.Icons; @@ -21,41 +23,38 @@ */ public class ControlTreeNode extends TreeNode { - /** - * Instantiates a new Control model node. - * - * @param element the element - * @param objectId the object id - */ public ControlTreeNode(@NotNull final T element, final long objectId) { super(element, objectId); } @Override + @FXThread public void fillContextMenu(@NotNull final NodeTree nodeTree, @NotNull final ObservableList items) { items.add(new RemoveControlAction(nodeTree, this)); super.fillContextMenu(nodeTree, items); } - @Nullable @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { return Icons.GEAR_16; } @Override + @FXThread public boolean canCopy() { return true; } @Override + @FXThread public boolean canMove() { return true; } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { final T element = getElement(); diff --git a/src/main/java/com/ss/editor/ui/control/model/node/control/SkeletonControlTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/control/SkeletonControlTreeNode.java index 250165c9..3066ea87 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/control/SkeletonControlTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/control/SkeletonControlTreeNode.java @@ -2,6 +2,8 @@ import com.jme3.animation.SkeletonControl; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; import javafx.scene.image.Image; import org.jetbrains.annotations.NotNull; @@ -14,25 +16,19 @@ */ public class SkeletonControlTreeNode extends ControlTreeNode { - /** - * Instantiates a new Skeleton control model node. - * - * @param element the element - * @param objectId the object id - */ public SkeletonControlTreeNode(@NotNull final SkeletonControl element, final long objectId) { super(element, objectId); } - @Nullable @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { return Icons.SKELETON_16; } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { return Messages.MODEL_FILE_EDITOR_NODE_SKELETON_CONTROL; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/node/control/anim/AnimationControlTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/control/anim/AnimationControlTreeNode.java index 259fa305..1b1aeb01 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/control/anim/AnimationControlTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/control/anim/AnimationControlTreeNode.java @@ -3,6 +3,8 @@ import com.jme3.animation.AnimControl; import com.jme3.animation.LoopMode; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.model.node.control.ControlTreeNode; import com.ss.editor.ui.control.model.tree.ModelNodeTree; @@ -37,12 +39,6 @@ public class AnimationControlTreeNode extends ControlTreeNode { */ private float speed; - /** - * Instantiates a new Animation control model node. - * - * @param element the element - * @param objectId the object id - */ public AnimationControlTreeNode(@NotNull final AnimControl element, final long objectId) { super(element, objectId); this.loopMode = LoopMode.Loop; @@ -50,6 +46,7 @@ public AnimationControlTreeNode(@NotNull final AnimControl element, final long o } @Override + @FXThread public void fillContextMenu(@NotNull final NodeTree nodeTree, @NotNull final ObservableList items) { items.add(new PlaySettingsAction(nodeTree, this)); @@ -62,6 +59,7 @@ public void fillContextMenu(@NotNull final NodeTree nodeTree, * @param loopMode the loop mode. * @param speed the animation speed. */ + @FXThread public void updateSettings(@NotNull LoopMode loopMode, final float speed) { this.loopMode = loopMode; this.speed = speed; @@ -72,6 +70,7 @@ public void updateSettings(@NotNull LoopMode loopMode, final float speed) { * * @return the animation speed. */ + @FXThread public float getSpeed() { return speed; } @@ -81,31 +80,31 @@ public float getSpeed() { * * @return the loop mode. */ - @NotNull - public LoopMode getLoopMode() { + @FXThread + public @NotNull LoopMode getLoopMode() { return loopMode; } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { return Messages.MODEL_FILE_EDITOR_NODE_ANIM_CONTROL; } - @Nullable @Override - public Image getIcon() { + public @Nullable Image getIcon() { return Icons.ANIMATION_16; } @Override + @FXThread public boolean hasChildren(@NotNull final NodeTree nodeTree) { return nodeTree instanceof ModelNodeTree; } - @NotNull @Override - public Array> getChildren(@NotNull final NodeTree nodeTree) { + @FXThread + public @NotNull Array> getChildren(@NotNull final NodeTree nodeTree) { final Array> result = ArrayFactory.newArray(TreeNode.class); @@ -119,6 +118,7 @@ public Array> getChildren(@NotNull final NodeTree nodeTree) { } @Override + @FXThread public void notifyChildPreAdd(@NotNull final TreeNode treeNode) { final AnimationTreeNode animationModelNode = (AnimationTreeNode) treeNode; diff --git a/src/main/java/com/ss/editor/ui/control/model/node/control/anim/AnimationTrackTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/control/anim/AnimationTrackTreeNode.java index 15880acc..03c128b1 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/control/anim/AnimationTrackTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/control/anim/AnimationTrackTreeNode.java @@ -2,6 +2,8 @@ import com.jme3.animation.AnimControl; import com.jme3.animation.Track; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.control.tree.node.TreeNode; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -26,12 +28,6 @@ public abstract class AnimationTrackTreeNode extends TreeNode { */ private int channel; - /** - * Instantiates a new Animation model node. - * - * @param element the element - * @param objectId the object id - */ public AnimationTreeNode(@NotNull final Animation element, final long objectId) { super(element, objectId); this.channel = -1; } @Override + @FXThread public void fillContextMenu(@NotNull final NodeTree nodeTree, @NotNull final ObservableList items) { @@ -93,6 +90,7 @@ public void fillContextMenu(@NotNull final NodeTree nodeTree, } @Override + @FXThread public boolean hasChildren(@NotNull final NodeTree nodeTree) { final Animation element = getElement(); @@ -101,9 +99,9 @@ public boolean hasChildren(@NotNull final NodeTree nodeTree) { return tracks != null && tracks.length > 0 && nodeTree instanceof ModelNodeTree; } - @NotNull @Override - public Array> getChildren(@NotNull final NodeTree nodeTree) { + @FXThread + public @NotNull Array> getChildren(@NotNull final NodeTree nodeTree) { final Animation element = getElement(); final Track[] tracks = element.getTracks(); @@ -115,11 +113,13 @@ public Array> getChildren(@NotNull final NodeTree nodeTree) { } @Override + @FXThread public boolean canEditName() { return true; } @Override + @FXThread public void changeName(@NotNull final NodeTree nodeTree, @NotNull final String newName) { if (StringUtils.equals(getName(), newName)) return; @@ -138,8 +138,8 @@ public void changeName(@NotNull final NodeTree nodeTree, @NotNull final Strin * * @return the node of an animation control. */ - @Nullable - public AnimationControlTreeNode getControlModelNode() { + @FXThread + public @Nullable AnimationControlTreeNode getControlModelNode() { return controlModelNode; } @@ -148,6 +148,7 @@ public AnimationControlTreeNode getControlModelNode() { * * @param controlModelNode the node of an animation control. */ + @FXThread public void setControlModelNode(@Nullable final AnimationControlTreeNode controlModelNode) { this.controlModelNode = controlModelNode; } @@ -157,8 +158,8 @@ public void setControlModelNode(@Nullable final AnimationControlTreeNode control * * @return the animation control. */ - @Nullable - public AnimControl getControl() { + @FXThread + public @Nullable AnimControl getControl() { return control; } @@ -167,13 +168,14 @@ public AnimControl getControl() { * * @param control the animation control. */ + @FXThread public void setControl(@Nullable final AnimControl control) { this.control = control; } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { return getElement().getName(); } @@ -182,6 +184,7 @@ public String getName() { * * @return the index of playing animation. */ + @FXThread public int getChannel() { return channel; } @@ -191,6 +194,7 @@ public int getChannel() { * * @param channel the index of playing animation. */ + @FXThread public void setChannel(final int channel) { this.channel = channel; } @@ -200,6 +204,7 @@ public void setChannel(final int channel) { * * @return the speed */ + @FXThread public float getSpeed() { return speed; } @@ -209,18 +214,20 @@ public float getSpeed() { * * @param speed the speed */ + @FXThread public void setSpeed(final float speed) { this.speed = speed; } - @Nullable @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { if (getChannel() < 0) return Icons.PLAY_16; return getSpeed() < 0.0001F ? Icons.PAUSE_16 : Icons.STOP_16; } @Override + @FXThread public void notifyChildPreAdd(@NotNull final TreeNode treeNode) { final AnimationTrackTreeNode animationTrackModelNode = (AnimationTrackTreeNode) treeNode; animationTrackModelNode.setControl(getControl()); diff --git a/src/main/java/com/ss/editor/ui/control/model/node/control/motion/MotionEventTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/control/motion/MotionEventTreeNode.java index 1098339f..4a3402ac 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/control/motion/MotionEventTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/control/motion/MotionEventTreeNode.java @@ -3,15 +3,17 @@ import com.jme3.cinematic.MotionPath; import com.jme3.cinematic.events.MotionEvent; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.model.node.control.ControlTreeNode; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; +import com.ss.rlib.util.array.Array; +import com.ss.rlib.util.array.ArrayFactory; import javafx.scene.image.Image; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import com.ss.rlib.util.array.Array; -import com.ss.rlib.util.array.ArrayFactory; /** * The implementation of the {@link ControlTreeNode} to show a {@link MotionEvent} in the tree. @@ -20,31 +22,25 @@ */ public class MotionEventTreeNode extends ControlTreeNode { - /** - * Instantiates a new Motion event model node. - * - * @param element the element - * @param objectId the object id - */ public MotionEventTreeNode(@NotNull final MotionEvent element, final long objectId) { super(element, objectId); } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { return Messages.MODEL_FILE_EDITOR_NODE_MOTION_CONTROL; } - @Nullable @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { return Icons.MOTION_16; } - @NotNull @Override - public Array> getChildren(@NotNull final NodeTree nodeTree) { + @FXThread + public @NotNull Array> getChildren(@NotNull final NodeTree nodeTree) { final MotionPath path = getElement().getPath(); final Array> result = ArrayFactory.newArray(TreeNode.class); @@ -54,6 +50,7 @@ public Array> getChildren(@NotNull final NodeTree nodeTree) { } @Override + @FXThread public boolean hasChildren(@NotNull final NodeTree nodeTree) { return true; } diff --git a/src/main/java/com/ss/editor/ui/control/model/node/control/motion/MotionPathTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/control/motion/MotionPathTreeNode.java index 332ed6b7..46bbb83c 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/control/motion/MotionPathTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/control/motion/MotionPathTreeNode.java @@ -3,6 +3,8 @@ import static com.ss.rlib.util.ObjectUtils.notNull; import com.jme3.cinematic.MotionPath; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.model.node.PositionTreeNode; import com.ss.editor.ui.control.model.tree.ModelNodeTree; @@ -21,31 +23,25 @@ */ public class MotionPathTreeNode extends TreeNode { - /** - * Instantiates a new Motion path model node. - * - * @param element the element - * @param objectId the object id - */ public MotionPathTreeNode(@NotNull final MotionPath element, final long objectId) { super(element, objectId); } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { return Messages.MODEL_FILE_EDITOR_NODE_MOTION_PATH; } - @Nullable @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { return Icons.PATH_16; } - @NotNull @Override - public Array> getChildren(@NotNull final NodeTree nodeTree) { + @FXThread + public @NotNull Array> getChildren(@NotNull final NodeTree nodeTree) { final MotionPath element = getElement(); final int wayPoints = element.getNbWayPoints(); @@ -62,6 +58,7 @@ public Array> getChildren(@NotNull final NodeTree nodeTree) { } @Override + @FXThread public boolean hasChildren(@NotNull final NodeTree nodeTree) { return nodeTree instanceof ModelNodeTree; } diff --git a/src/main/java/com/ss/editor/ui/control/model/node/control/physics/CharacterControlTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/control/physics/CharacterControlTreeNode.java index 116e2c5e..059df6a3 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/control/physics/CharacterControlTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/control/physics/CharacterControlTreeNode.java @@ -2,6 +2,8 @@ import com.jme3.bullet.control.CharacterControl; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; import javafx.scene.image.Image; import org.jetbrains.annotations.NotNull; @@ -14,25 +16,19 @@ */ public class CharacterControlTreeNode extends PhysicsObjectTreeNode { - /** - * Instantiates a new Character control model node. - * - * @param element the element - * @param objectId the object id - */ public CharacterControlTreeNode(@NotNull final CharacterControl element, final long objectId) { super(element, objectId); } - @Nullable @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { return Icons.CHARACTER_16; } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { return Messages.MODEL_FILE_EDITOR_NODE_CHARACTER_CONTROL; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/node/control/physics/RagdollControlTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/control/physics/RagdollControlTreeNode.java index b937eab2..7f8a8108 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/control/physics/RagdollControlTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/control/physics/RagdollControlTreeNode.java @@ -3,6 +3,8 @@ import com.jme3.bullet.control.KinematicRagdollControl; import com.jme3.bullet.control.VehicleControl; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.model.node.control.ControlTreeNode; import javafx.scene.image.Image; @@ -16,25 +18,19 @@ */ public class RagdollControlTreeNode extends ControlTreeNode { - /** - * Instantiates a new Ragdoll control model node. - * - * @param element the element - * @param objectId the object id - */ public RagdollControlTreeNode(@NotNull final KinematicRagdollControl element, final long objectId) { super(element, objectId); } - @Nullable @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { return Icons.DOLL_16; } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { return Messages.MODEL_FILE_EDITOR_NODE_RAGDOLL_CONTROL; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/node/control/physics/RigidBodyControlTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/control/physics/RigidBodyControlTreeNode.java index f5562024..90d76fc2 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/control/physics/RigidBodyControlTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/control/physics/RigidBodyControlTreeNode.java @@ -2,6 +2,8 @@ import com.jme3.bullet.control.RigidBodyControl; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.model.tree.action.control.physics.ReactivatePhysicsControl; import com.ss.editor.ui.control.tree.NodeTree; @@ -18,19 +20,13 @@ */ public class RigidBodyControlTreeNode extends PhysicsObjectTreeNode { - /** - * Instantiates a new Rigid body control model node. - * - * @param element the element - * @param objectId the object id - */ public RigidBodyControlTreeNode(@NotNull final RigidBodyControl element, final long objectId) { super(element, objectId); } - @Nullable @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { final RigidBodyControl element = getElement(); @@ -41,9 +37,9 @@ public Image getIcon() { return Icons.RIGID_BODY_16; } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { final RigidBodyControl element = getElement(); @@ -55,6 +51,7 @@ public String getName() { } @Override + @FXThread public void fillContextMenu(@NotNull final NodeTree nodeTree, @NotNull final ObservableList items) { diff --git a/src/main/java/com/ss/editor/ui/control/model/node/control/physics/vehicle/VehicleControlTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/control/physics/vehicle/VehicleControlTreeNode.java index 3ea6d989..554c00c2 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/control/physics/vehicle/VehicleControlTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/control/physics/vehicle/VehicleControlTreeNode.java @@ -2,6 +2,8 @@ import com.jme3.bullet.control.VehicleControl; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.model.node.control.physics.PhysicsObjectTreeNode; import com.ss.editor.ui.control.model.tree.action.control.physics.vehicle.CreateVehicleWheelAction; @@ -19,29 +21,24 @@ */ public class VehicleControlTreeNode extends PhysicsObjectTreeNode { - /** - * Instantiates a new Vehicle control model node. - * - * @param element the element - * @param objectId the object id - */ public VehicleControlTreeNode(@NotNull final VehicleControl element, final long objectId) { super(element, objectId); } - @Nullable @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { return Icons.VEHICLE_16; } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { return Messages.MODEL_FILE_EDITOR_NODE_VEHICLE_CONTROL; } @Override + @FXThread public void fillContextMenu(@NotNull final NodeTree nodeTree, @NotNull final ObservableList items) { diff --git a/src/main/java/com/ss/editor/ui/control/model/node/control/physics/vehicle/VehicleWheelTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/control/physics/vehicle/VehicleWheelTreeNode.java index ff89c35a..402d9bb3 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/control/physics/vehicle/VehicleWheelTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/control/physics/vehicle/VehicleWheelTreeNode.java @@ -3,6 +3,8 @@ import com.jme3.bullet.objects.VehicleWheel; import com.jme3.scene.Spatial; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.model.tree.action.control.physics.vehicle.RemoveVehicleWheelAction; import com.ss.editor.ui.control.tree.NodeTree; @@ -20,25 +22,19 @@ */ public class VehicleWheelTreeNode extends TreeNode { - /** - * Instantiates a new Vehicle wheel model node. - * - * @param element the element - * @param objectId the object id - */ public VehicleWheelTreeNode(@NotNull final VehicleWheel element, final long objectId) { super(element, objectId); } - @Nullable @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { return Icons.WHEEL_16; } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { final VehicleWheel element = getElement(); final Spatial wheelSpatial = element.getWheelSpatial(); return wheelSpatial != null ? Messages.MODEL_FILE_EDITOR_NODE_WHEEL + " [" + wheelSpatial.getName() + "]" : @@ -46,6 +42,7 @@ public String getName() { } @Override + @FXThread public void fillContextMenu(@NotNull final NodeTree nodeTree, @NotNull final ObservableList items) { super.fillContextMenu(nodeTree, items); diff --git a/src/main/java/com/ss/editor/ui/control/model/node/light/AmbientLightTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/light/AmbientLightTreeNode.java index ba97a27f..cd4d0f20 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/light/AmbientLightTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/light/AmbientLightTreeNode.java @@ -2,6 +2,8 @@ import com.jme3.light.AmbientLight; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; import org.jetbrains.annotations.NotNull; @@ -17,25 +19,19 @@ */ public class AmbientLightTreeNode extends LightTreeNode { - /** - * Instantiates a new Ambient light model node. - * - * @param element the element - * @param objectId the object id - */ public AmbientLightTreeNode(@NotNull final AmbientLight element, final long objectId) { super(element, objectId); } - @Nullable @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { return Icons.AMBIENT_16; } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { final AmbientLight element = getElement(); final String name = element.getName(); return StringUtils.isEmpty(name) ? Messages.MODEL_FILE_EDITOR_NODE_AMBIENT_LIGHT : name; diff --git a/src/main/java/com/ss/editor/ui/control/model/node/light/DirectionalLightTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/light/DirectionalLightTreeNode.java index 6cdeb5a2..2f673f2d 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/light/DirectionalLightTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/light/DirectionalLightTreeNode.java @@ -2,14 +2,14 @@ import com.jme3.light.DirectionalLight; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; - +import com.ss.rlib.util.StringUtils; +import javafx.scene.image.Image; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import javafx.scene.image.Image; -import com.ss.rlib.util.StringUtils; - /** * The implementation of {@link LightTreeNode} to present direction lights. * @@ -17,25 +17,19 @@ */ public class DirectionalLightTreeNode extends LightTreeNode { - /** - * Instantiates a new Directional light model node. - * - * @param element the element - * @param objectId the object id - */ public DirectionalLightTreeNode(@NotNull final DirectionalLight element, final long objectId) { super(element, objectId); } - @Nullable @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { return Icons.SUN_16; } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { final DirectionalLight element = getElement(); final String name = element.getName(); return StringUtils.isEmpty(name) ? Messages.MODEL_FILE_EDITOR_NODE_DIRECTION_LIGHT : name; diff --git a/src/main/java/com/ss/editor/ui/control/model/node/light/LightProbeTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/light/LightProbeTreeNode.java index a1deb403..297f25c5 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/light/LightProbeTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/light/LightProbeTreeNode.java @@ -2,30 +2,24 @@ import com.jme3.light.LightProbe; import com.ss.editor.Messages; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.control.tree.node.TreeNode; - import org.jetbrains.annotations.NotNull; /** - * Реализация узла с цветопробой для PBR. + * The tree node to present {@link LightProbe} * - * @author Ronn + * @author JavaSaBr */ public class LightProbeTreeNode extends TreeNode { - /** - * Instantiates a new Light probe model node. - * - * @param element the element - * @param objectId the object id - */ - public LightProbeTreeNode(final LightProbe element, final long objectId) { + public LightProbeTreeNode(@NotNull final LightProbe element, final long objectId) { super(element, objectId); } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { return Messages.MODEL_FILE_EDITOR_NODE_LIGHT_PROBE; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/node/light/LightTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/light/LightTreeNode.java index a076478e..8f9f56df 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/light/LightTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/light/LightTreeNode.java @@ -1,19 +1,19 @@ package com.ss.editor.ui.control.model.node.light; import com.jme3.light.Light; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.model.tree.action.RemoveLightAction; import com.ss.editor.ui.control.model.tree.action.RenameNodeAction; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - +import com.ss.rlib.util.StringUtils; import javafx.collections.ObservableList; import javafx.scene.control.MenuItem; import javafx.scene.image.Image; -import com.ss.rlib.util.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * The base implementation of {@link TreeNode} to present lights. @@ -23,42 +23,39 @@ */ public class LightTreeNode extends TreeNode { - /** - * Instantiates a new Light model node. - * - * @param element the element - * @param objectId the object id - */ public LightTreeNode(@NotNull final T element, final long objectId) { super(element, objectId); } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { final T element = getElement(); final String name = element.getName(); return StringUtils.isEmpty(name) ? element.getClass().getSimpleName() : name; } @Override + @FXThread public void changeName(@NotNull final NodeTree nodeTree, @NotNull final String newName) { final T element = getElement(); element.setName(newName); } @Override + @FXThread public boolean canEditName() { return true; } - @Nullable @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { return Icons.LIGHT_16; } @Override + @FXThread public void fillContextMenu(@NotNull final NodeTree nodeTree, @NotNull final ObservableList items) { items.add(new RemoveLightAction(nodeTree, this)); items.add(new RenameNodeAction(nodeTree, this)); diff --git a/src/main/java/com/ss/editor/ui/control/model/node/light/PointLightTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/light/PointLightTreeNode.java index 27e52820..bc5c78b6 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/light/PointLightTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/light/PointLightTreeNode.java @@ -2,14 +2,14 @@ import com.jme3.light.PointLight; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; - +import com.ss.rlib.util.StringUtils; +import javafx.scene.image.Image; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import javafx.scene.image.Image; -import com.ss.rlib.util.StringUtils; - /** * The implementation of {@link LightTreeNode} to present point lights. * @@ -17,25 +17,19 @@ */ public class PointLightTreeNode extends LightTreeNode { - /** - * Instantiates a new Point light model node. - * - * @param element the element - * @param objectId the object id - */ public PointLightTreeNode(@NotNull final PointLight element, final long objectId) { super(element, objectId); } - @Nullable @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { return Icons.POINT_LIGHT_16; } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { final PointLight element = getElement(); final String name = element.getName(); return StringUtils.isEmpty(name) ? Messages.MODEL_FILE_EDITOR_NODE_POINT_LIGHT : name; diff --git a/src/main/java/com/ss/editor/ui/control/model/node/light/SpotLightTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/light/SpotLightTreeNode.java index 2fc43f27..27c15a3e 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/light/SpotLightTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/light/SpotLightTreeNode.java @@ -2,14 +2,14 @@ import com.jme3.light.SpotLight; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; - +import com.ss.rlib.util.StringUtils; +import javafx.scene.image.Image; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import javafx.scene.image.Image; -import com.ss.rlib.util.StringUtils; - /** * The implementation of {@link LightTreeNode} to present spot lights. * @@ -17,25 +17,19 @@ */ public class SpotLightTreeNode extends LightTreeNode { - /** - * Instantiates a new Spot light model node. - * - * @param element the element - * @param objectId the object id - */ public SpotLightTreeNode(@NotNull final SpotLight element, final long objectId) { super(element, objectId); } - @Nullable @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { return Icons.LAMP_16; } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { final SpotLight element = getElement(); final String name = element.getName(); return StringUtils.isEmpty(name) ? Messages.MODEL_FILE_EDITOR_NODE_SPOT_LIGHT : name; diff --git a/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/BoxCollisionShapeTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/BoxCollisionShapeTreeNode.java index d47afaee..9107a6ef 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/BoxCollisionShapeTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/BoxCollisionShapeTreeNode.java @@ -2,6 +2,7 @@ import com.jme3.bullet.collision.shapes.BoxCollisionShape; import com.ss.editor.Messages; +import com.ss.editor.annotation.FromAnyThread; import org.jetbrains.annotations.NotNull; /** @@ -11,19 +12,13 @@ */ public class BoxCollisionShapeTreeNode extends CollisionShapeTreeNode { - /** - * Instantiates a new Box collision shape model node. - * - * @param element the element - * @param objectId the object id - */ public BoxCollisionShapeTreeNode(@NotNull final BoxCollisionShape element, final long objectId) { super(element, objectId); } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { return Messages.MODEL_FILE_EDITOR_NODE_BOX_COLLISION_SHAPE; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/CapsuleCollisionShapeTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/CapsuleCollisionShapeTreeNode.java index 7d158d06..ff272e39 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/CapsuleCollisionShapeTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/CapsuleCollisionShapeTreeNode.java @@ -2,6 +2,8 @@ import com.jme3.bullet.collision.shapes.CapsuleCollisionShape; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; import javafx.scene.image.Image; import org.jetbrains.annotations.NotNull; @@ -14,25 +16,19 @@ */ public class CapsuleCollisionShapeTreeNode extends CollisionShapeTreeNode { - /** - * Instantiates a new Capsule collision shape model node. - * - * @param element the element - * @param objectId the object id - */ public CapsuleCollisionShapeTreeNode(@NotNull final CapsuleCollisionShape element, final long objectId) { super(element, objectId); } - @Nullable @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { return Icons.CAPSULE_16; } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { return Messages.MODEL_FILE_EDITOR_NODE_CAPSULE_COLLISION_SHAPE; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/ChildCollisionShapeTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/ChildCollisionShapeTreeNode.java index 6e925472..4533633e 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/ChildCollisionShapeTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/ChildCollisionShapeTreeNode.java @@ -3,14 +3,16 @@ import com.jme3.bullet.collision.shapes.CollisionShape; import com.jme3.bullet.collision.shapes.infos.ChildCollisionShape; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; +import com.ss.rlib.util.array.Array; +import com.ss.rlib.util.array.ArrayFactory; import javafx.scene.image.Image; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import com.ss.rlib.util.array.Array; -import com.ss.rlib.util.array.ArrayFactory; /** * The implementation of the {@link TreeNode} to show a {@link ChildCollisionShape} in the tree. @@ -19,19 +21,13 @@ */ public class ChildCollisionShapeTreeNode extends TreeNode { - /** - * Instantiates a new Child collision shape model node. - * - * @param element the element - * @param objectId the object id - */ public ChildCollisionShapeTreeNode(@NotNull final ChildCollisionShape element, final long objectId) { super(element, objectId); } - @NotNull @Override - public Array> getChildren(@NotNull final NodeTree nodeTree) { + @FXThread + public @NotNull Array> getChildren(@NotNull final NodeTree nodeTree) { final ChildCollisionShape element = getElement(); final CollisionShape shape = element.shape; @@ -43,19 +39,21 @@ public Array> getChildren(@NotNull final NodeTree nodeTree) { } @Override + @FXThread public boolean hasChildren(@NotNull final NodeTree nodeTree) { return true; } - @Nullable + @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { return Icons.NODE_16; } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { return Messages.MODEL_FILE_EDITOR_NODE_CHILD_COLLISION_SHAPE; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/CollisionShapeTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/CollisionShapeTreeNode.java index 05124c64..2e010736 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/CollisionShapeTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/CollisionShapeTreeNode.java @@ -1,6 +1,8 @@ package com.ss.editor.ui.control.model.node.physics.shape; import com.jme3.bullet.collision.shapes.CollisionShape; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.tree.node.TreeNode; import javafx.scene.image.Image; @@ -15,25 +17,19 @@ */ public class CollisionShapeTreeNode extends TreeNode { - /** - * Instantiates a new Collision shape model node. - * - * @param element the element - * @param objectId the object id - */ public CollisionShapeTreeNode(@NotNull final T element, final long objectId) { super(element, objectId); } - @Nullable @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { return Icons.GEOMETRY_16; } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { final T element = getElement(); return element.getClass().getSimpleName(); } diff --git a/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/ComputedCollisionShapeTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/ComputedCollisionShapeTreeNode.java index 6d960ec2..447abb9f 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/ComputedCollisionShapeTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/ComputedCollisionShapeTreeNode.java @@ -3,6 +3,8 @@ import com.jme3.bullet.collision.shapes.CompoundCollisionShape; import com.jme3.bullet.collision.shapes.infos.ChildCollisionShape; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; @@ -21,19 +23,13 @@ */ public class ComputedCollisionShapeTreeNode extends CollisionShapeTreeNode { - /** - * Instantiates a new Computed collision shape model node. - * - * @param element the element - * @param objectId the object id - */ public ComputedCollisionShapeTreeNode(@NotNull final CompoundCollisionShape element, final long objectId) { super(element, objectId); } - @NotNull @Override - public Array> getChildren(@NotNull final NodeTree nodeTree) { + @FXThread + public @NotNull Array> getChildren(@NotNull final NodeTree nodeTree) { final CompoundCollisionShape element = getElement(); final List children = element.getChildren(); @@ -44,19 +40,20 @@ public Array> getChildren(@NotNull final NodeTree nodeTree) { } @Override + @FXThread public boolean hasChildren(@NotNull final NodeTree nodeTree) { return true; } - @Nullable @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { return Icons.ATOM_16; } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { return Messages.MODEL_FILE_EDITOR_NODE_COMPUTED_COLLISION_SHAPE; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/ConeCollisionShapeTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/ConeCollisionShapeTreeNode.java index 7aaaef62..b6654f18 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/ConeCollisionShapeTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/ConeCollisionShapeTreeNode.java @@ -2,6 +2,8 @@ import com.jme3.bullet.collision.shapes.ConeCollisionShape; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; import javafx.scene.image.Image; import org.jetbrains.annotations.NotNull; @@ -14,25 +16,19 @@ */ public class ConeCollisionShapeTreeNode extends CollisionShapeTreeNode { - /** - * Instantiates a new Cone collision shape model node. - * - * @param element the element - * @param objectId the object id - */ public ConeCollisionShapeTreeNode(@NotNull final ConeCollisionShape element, final long objectId) { super(element, objectId); } - @Nullable @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { return Icons.CONE_16; } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { return Messages.MODEL_FILE_EDITOR_NODE_CONE_COLLISION_SHAPE; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/CylinderCollisionShapeTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/CylinderCollisionShapeTreeNode.java index 32195cb6..b0e26ba7 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/CylinderCollisionShapeTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/CylinderCollisionShapeTreeNode.java @@ -2,6 +2,8 @@ import com.jme3.bullet.collision.shapes.CylinderCollisionShape; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; import javafx.scene.image.Image; import org.jetbrains.annotations.NotNull; @@ -14,25 +16,19 @@ */ public class CylinderCollisionShapeTreeNode extends CollisionShapeTreeNode { - /** - * Instantiates a new Cylinder collision shape model node. - * - * @param element the element - * @param objectId the object id - */ public CylinderCollisionShapeTreeNode(@NotNull final CylinderCollisionShape element, final long objectId) { super(element, objectId); } - @Nullable @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { return Icons.CYLINDER_16; } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { return Messages.MODEL_FILE_EDITOR_NODE_CYLINDER_COLLISION_SHAPE; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/GImpactCollisionShapeTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/GImpactCollisionShapeTreeNode.java index 2d681cdf..47bbd0fa 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/GImpactCollisionShapeTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/GImpactCollisionShapeTreeNode.java @@ -2,6 +2,8 @@ import com.jme3.bullet.collision.shapes.GImpactCollisionShape; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; import javafx.scene.image.Image; import org.jetbrains.annotations.NotNull; @@ -14,25 +16,19 @@ */ public class GImpactCollisionShapeTreeNode extends CollisionShapeTreeNode { - /** - * Instantiates a new G impact collision shape model node. - * - * @param element the element - * @param objectId the object id - */ public GImpactCollisionShapeTreeNode(@NotNull final GImpactCollisionShape element, final long objectId) { super(element, objectId); } - @Nullable @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { return Icons.MESH_16; } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { return Messages.MODEL_FILE_EDITOR_NODE_GIMPACT_COLLISION_SHAPE; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/HeightFieldCollisionShapeTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/HeightFieldCollisionShapeTreeNode.java index a15c859f..fbf2d48b 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/HeightFieldCollisionShapeTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/HeightFieldCollisionShapeTreeNode.java @@ -2,6 +2,8 @@ import com.jme3.bullet.collision.shapes.HeightfieldCollisionShape; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; import javafx.scene.image.Image; import org.jetbrains.annotations.NotNull; @@ -14,25 +16,19 @@ */ public class HeightFieldCollisionShapeTreeNode extends CollisionShapeTreeNode { - /** - * Instantiates a new Height field collision shape model node. - * - * @param element the element - * @param objectId the object id - */ public HeightFieldCollisionShapeTreeNode(@NotNull final HeightfieldCollisionShape element, final long objectId) { super(element, objectId); } - @Nullable @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { return Icons.TERRAIN_16; } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { return Messages.MODEL_FILE_EDITOR_NODE_HEIGHT_FIELD_COLLISION_SHAPE; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/HullCollisionShapeTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/HullCollisionShapeTreeNode.java index 32e6fff6..d714547d 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/HullCollisionShapeTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/HullCollisionShapeTreeNode.java @@ -2,6 +2,8 @@ import com.jme3.bullet.collision.shapes.HullCollisionShape; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; import javafx.scene.image.Image; import org.jetbrains.annotations.NotNull; @@ -14,25 +16,19 @@ */ public class HullCollisionShapeTreeNode extends CollisionShapeTreeNode { - /** - * Instantiates a new Hull collision shape model node. - * - * @param element the element - * @param objectId the object id - */ public HullCollisionShapeTreeNode(@NotNull final HullCollisionShape element, final long objectId) { super(element, objectId); } - @Nullable @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { return Icons.MESH_16; } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { return Messages.MODEL_FILE_EDITOR_NODE_HULL_COLLISION_SHAPE; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/MeshCollisionShapeTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/MeshCollisionShapeTreeNode.java index 575ea715..817b0e21 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/MeshCollisionShapeTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/MeshCollisionShapeTreeNode.java @@ -2,6 +2,8 @@ import com.jme3.bullet.collision.shapes.MeshCollisionShape; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; import javafx.scene.image.Image; import org.jetbrains.annotations.NotNull; @@ -14,25 +16,19 @@ */ public class MeshCollisionShapeTreeNode extends CollisionShapeTreeNode { - /** - * Instantiates a new Mesh collision shape model node. - * - * @param element the element - * @param objectId the object id - */ public MeshCollisionShapeTreeNode(@NotNull final MeshCollisionShape element, final long objectId) { super(element, objectId); } - @Nullable @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { return Icons.MESH_16; } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { return Messages.MODEL_FILE_EDITOR_NODE_MESH_COLLISION_SHAPE; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/PlaneCollisionShapeTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/PlaneCollisionShapeTreeNode.java index 06906186..2ec8df3f 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/PlaneCollisionShapeTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/PlaneCollisionShapeTreeNode.java @@ -2,6 +2,8 @@ import com.jme3.bullet.collision.shapes.PlaneCollisionShape; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; import javafx.scene.image.Image; import org.jetbrains.annotations.NotNull; @@ -14,25 +16,19 @@ */ public class PlaneCollisionShapeTreeNode extends CollisionShapeTreeNode { - /** - * Instantiates a new Plane collision shape model node. - * - * @param element the element - * @param objectId the object id - */ public PlaneCollisionShapeTreeNode(@NotNull final PlaneCollisionShape element, final long objectId) { super(element, objectId); } - @Nullable @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { return Icons.PLANE_16; } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { return Messages.MODEL_FILE_EDITOR_NODE_PLANE_COLLISION_SHAPE; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/SphereCollisionShapeTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/SphereCollisionShapeTreeNode.java index 7aa41577..e0ac503d 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/SphereCollisionShapeTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/physics/shape/SphereCollisionShapeTreeNode.java @@ -2,6 +2,8 @@ import com.jme3.bullet.collision.shapes.SphereCollisionShape; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; import javafx.scene.image.Image; import org.jetbrains.annotations.NotNull; @@ -14,25 +16,19 @@ */ public class SphereCollisionShapeTreeNode extends CollisionShapeTreeNode { - /** - * Instantiates a new Sphere collision shape model node. - * - * @param element the element - * @param objectId the object id - */ public SphereCollisionShapeTreeNode(@NotNull final SphereCollisionShape element, final long objectId) { super(element, objectId); } - @Nullable @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { return Icons.SPHERE_16; } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { return Messages.MODEL_FILE_EDITOR_NODE_SPHERE_COLLISION_SHAPE; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/node/spatial/MaterialTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/spatial/MaterialTreeNode.java index 0d2d7b11..44a6b74e 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/spatial/MaterialTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/spatial/MaterialTreeNode.java @@ -5,6 +5,8 @@ import com.jme3.scene.AssetLinkNode; import com.jme3.scene.Spatial; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.model.tree.action.MakeAsEmbeddedMaterialAction; import com.ss.editor.ui.control.model.tree.action.SaveAsMaterialAction; @@ -29,16 +31,19 @@ public MaterialTreeNode(@NotNull final Material element, final long objectId) { } @Override + @FXThread public @Nullable Image getIcon() { return Icons.MATERIAL_16; } @Override + @FromAnyThread public @NotNull String getName() { return Messages.MODEL_FILE_EDITOR_NODE_MATERIAL; } @Override + @FXThread public void fillContextMenu(@NotNull final NodeTree nodeTree, @NotNull final ObservableList items) { super.fillContextMenu(nodeTree, items); @@ -58,11 +63,13 @@ public void fillContextMenu(@NotNull final NodeTree nodeTree, @NotNull final } @Override + @FXThread public boolean hasChildren(@NotNull final NodeTree nodeTree) { return false; } @Override + @FXThread public boolean canCopy() { return true; } diff --git a/src/main/java/com/ss/editor/ui/control/model/node/spatial/MeshTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/spatial/MeshTreeNode.java index 978b1721..d80501fd 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/spatial/MeshTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/spatial/MeshTreeNode.java @@ -4,6 +4,8 @@ import com.jme3.scene.VertexBuffer; import com.jme3.util.IntMap; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; @@ -20,31 +22,25 @@ */ public class MeshTreeNode extends TreeNode { - /** - * Instantiates a new Mesh model node. - * - * @param element the element - * @param objectId the object id - */ public MeshTreeNode(@NotNull final Mesh element, final long objectId) { super(element, objectId); } - @Nullable @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { return Icons.MESH_16; } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { return Messages.MODEL_FILE_EDITOR_NODE_MESH; } - @NotNull @Override - public Array> getChildren(@NotNull final NodeTree nodeTree) { + @FXThread + public @NotNull Array> getChildren(@NotNull final NodeTree nodeTree) { final Array> result = ArrayFactory.newArray(TreeNode.class); @@ -56,11 +52,13 @@ public Array> getChildren(@NotNull final NodeTree nodeTree) { } @Override + @FXThread public boolean hasChildren(@NotNull final NodeTree nodeTree) { return true; } @Override + @FXThread public boolean canCopy() { return true; } diff --git a/src/main/java/com/ss/editor/ui/control/model/node/spatial/SpatialTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/spatial/SpatialTreeNode.java index f9291949..679900e9 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/spatial/SpatialTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/spatial/SpatialTreeNode.java @@ -15,6 +15,8 @@ import com.jme3.scene.Spatial; import com.jme3.scene.control.AbstractControl; import com.jme3.scene.control.Control; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.control.transform.EditorTransformSupport; import com.ss.editor.extension.scene.InvisibleObject; import com.ss.editor.model.undo.editor.ChangeConsumer; @@ -54,17 +56,12 @@ */ public class SpatialTreeNode extends TreeNode { - /** - * Instantiates a new Spatial model node. - * - * @param element the element - * @param objectId the object id - */ protected SpatialTreeNode(@NotNull final T element, final long objectId) { super(element, objectId); } @Override + @FXThread public void fillContextMenu(@NotNull final NodeTree nodeTree, @NotNull final ObservableList items) { if (!(nodeTree instanceof ModelNodeTree)) return; @@ -89,16 +86,19 @@ public void fillContextMenu(@NotNull final NodeTree nodeTree, } @Override + @FXThread public boolean canMove() { return true; } @Override + @FXThread public boolean canCopy() { return true; } @Override + @FXThread public boolean canAccept(@NotNull final TreeNode child, final boolean isCopy) { final Object element = child.getElement(); @@ -111,6 +111,7 @@ public boolean canAccept(@NotNull final TreeNode child, final boolean isCopy) } @Override + @FXThread public void accept(@NotNull final ChangeConsumer changeConsumer, @NotNull final Object object, final boolean isCopy) { @@ -134,6 +135,7 @@ public void accept(@NotNull final ChangeConsumer changeConsumer, @NotNull final } @Override + @FXThread public boolean canRemove() { final Node parent = getElement().getParent(); return parent != null && parent.getUserData(EditorTransformSupport.class.getName()) != Boolean.TRUE; @@ -145,8 +147,8 @@ public boolean canRemove() { * @param nodeTree the node tree * @return the menu */ - @Nullable - protected Menu createCreationMenu(@NotNull final NodeTree nodeTree) { + @FXThread + protected @Nullable Menu createCreationMenu(@NotNull final NodeTree nodeTree) { final T element = getElement(); @@ -189,29 +191,32 @@ protected Menu createCreationMenu(@NotNull final NodeTree nodeTree) { * @param nodeTree the node tree * @return the menu */ - @Nullable - protected Menu createToolMenu(final @NotNull NodeTree nodeTree) { + @FXThread + protected @Nullable Menu createToolMenu(final @NotNull NodeTree nodeTree) { return null; } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { final String name = getElement().getName(); return name == null ? "name is null" : name; } @Override + @FXThread public boolean canEditName() { return true; } @Override + @FXThread public boolean hasChildren(@NotNull final NodeTree nodeTree) { return nodeTree instanceof ModelNodeTree; } @Override + @FXThread public void changeName(@NotNull final NodeTree nodeTree, @NotNull final String newName) { if (StringUtils.equals(getName(), newName)) return; @@ -247,6 +252,7 @@ public Array> getChildren(@NotNull final NodeTree nodeTree) { } @Override + @FXThread public void add(@NotNull final TreeNode child) { super.add(child); @@ -262,6 +268,7 @@ public void add(@NotNull final TreeNode child) { } @Override + @FXThread public void remove(@NotNull final TreeNode child) { super.remove(child); diff --git a/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/influencer/DefaultParticleInfluencerTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/influencer/DefaultParticleInfluencerTreeNode.java index 64349947..92542c75 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/influencer/DefaultParticleInfluencerTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/influencer/DefaultParticleInfluencerTreeNode.java @@ -2,6 +2,7 @@ import com.jme3.effect.influencers.DefaultParticleInfluencer; import com.ss.editor.Messages; +import com.ss.editor.annotation.FromAnyThread; import org.jetbrains.annotations.NotNull; /** @@ -11,19 +12,13 @@ */ public class DefaultParticleInfluencerTreeNode extends ParticleInfluencerTreeNode { - /** - * Instantiates a new Default particle influencer model node. - * - * @param element the element - * @param objectId the object id - */ public DefaultParticleInfluencerTreeNode(@NotNull final DefaultParticleInfluencer element, final long objectId) { super(element, objectId); } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { return Messages.MODEL_FILE_EDITOR_NODE_PARTICLE_EMITTER_INFLUENCER_DEFAULT; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/influencer/EmptyParticleInfluencerTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/influencer/EmptyParticleInfluencerTreeNode.java index bb5bbb14..083c34c4 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/influencer/EmptyParticleInfluencerTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/influencer/EmptyParticleInfluencerTreeNode.java @@ -2,6 +2,7 @@ import com.jme3.effect.influencers.EmptyParticleInfluencer; import com.ss.editor.Messages; +import com.ss.editor.annotation.FromAnyThread; import org.jetbrains.annotations.NotNull; /** @@ -11,19 +12,13 @@ */ public class EmptyParticleInfluencerTreeNode extends ParticleInfluencerTreeNode { - /** - * Instantiates a new Empty particle influencer model node. - * - * @param element the element - * @param objectId the object id - */ public EmptyParticleInfluencerTreeNode(@NotNull final EmptyParticleInfluencer element, final long objectId) { super(element, objectId); } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { return Messages.MODEL_FILE_EDITOR_NODE_PARTICLE_EMITTER_INFLUENCER_EMPTY; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/influencer/ParticleInfluencerTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/influencer/ParticleInfluencerTreeNode.java index c9483173..9119aeb9 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/influencer/ParticleInfluencerTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/influencer/ParticleInfluencerTreeNode.java @@ -1,6 +1,8 @@ package com.ss.editor.ui.control.model.node.spatial.particle.emitter.influencer; import com.jme3.effect.influencers.ParticleInfluencer; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.tree.node.TreeNode; import javafx.scene.image.Image; @@ -14,25 +16,19 @@ */ public class ParticleInfluencerTreeNode extends TreeNode { - /** - * Instantiates a new Particle influencer model node. - * - * @param element the element - * @param objectId the object id - */ public ParticleInfluencerTreeNode(@NotNull final ParticleInfluencer element, final long objectId) { super(element, objectId); } - @Nullable @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { return Icons.INFLUENCER_16; } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { final ParticleInfluencer element = getElement(); return element.getClass().getSimpleName(); } diff --git a/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/influencer/RadialParticleInfluencerTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/influencer/RadialParticleInfluencerTreeNode.java index 4862af1d..0f8c8f3f 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/influencer/RadialParticleInfluencerTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/influencer/RadialParticleInfluencerTreeNode.java @@ -2,6 +2,7 @@ import com.jme3.effect.influencers.RadialParticleInfluencer; import com.ss.editor.Messages; +import com.ss.editor.annotation.FromAnyThread; import org.jetbrains.annotations.NotNull; /** @@ -11,19 +12,13 @@ */ public class RadialParticleInfluencerTreeNode extends ParticleInfluencerTreeNode { - /** - * Instantiates a new Radial particle influencer model node. - * - * @param element the element - * @param objectId the object id - */ public RadialParticleInfluencerTreeNode(@NotNull final RadialParticleInfluencer element, final long objectId) { super(element, objectId); } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { return Messages.MODEL_FILE_EDITOR_NODE_PARTICLE_EMITTER_INFLUENCER_RADIAL; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/shape/EmitterBoxShapeTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/shape/EmitterBoxShapeTreeNode.java index bbcfe2f3..e0ca5af9 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/shape/EmitterBoxShapeTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/shape/EmitterBoxShapeTreeNode.java @@ -2,6 +2,8 @@ import com.jme3.effect.shapes.EmitterBoxShape; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; import javafx.scene.image.Image; import org.jetbrains.annotations.NotNull; @@ -14,25 +16,19 @@ */ public class EmitterBoxShapeTreeNode extends EmitterShapeTreeNode { - /** - * Instantiates a new Emitter box shape model node. - * - * @param element the element - * @param objectId the object id - */ public EmitterBoxShapeTreeNode(@NotNull final EmitterBoxShape element, final long objectId) { super(element, objectId); } - @Nullable @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { return Icons.CUBE_16; } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { return Messages.MODEL_FILE_EDITOR_NODE_PARTICLE_EMITTER_SHAPE_BOX; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/shape/EmitterMeshConvexHullShapeTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/shape/EmitterMeshConvexHullShapeTreeNode.java index 2bef17c5..c045eccd 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/shape/EmitterMeshConvexHullShapeTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/shape/EmitterMeshConvexHullShapeTreeNode.java @@ -2,6 +2,7 @@ import com.jme3.effect.shapes.EmitterMeshConvexHullShape; import com.ss.editor.Messages; +import com.ss.editor.annotation.FromAnyThread; import org.jetbrains.annotations.NotNull; /** @@ -11,19 +12,13 @@ */ public class EmitterMeshConvexHullShapeTreeNode extends EmitterShapeTreeNode { - /** - * Instantiates a new Emitter mesh convex hull shape model node. - * - * @param element the element - * @param objectId the object id - */ public EmitterMeshConvexHullShapeTreeNode(@NotNull final EmitterMeshConvexHullShape element, final long objectId) { super(element, objectId); } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { return Messages.MODEL_FILE_EDITOR_NODE_PARTICLE_EMITTER_SHAPE_MESH_CONVEX_HULL; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/shape/EmitterMeshFaceShapeTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/shape/EmitterMeshFaceShapeTreeNode.java index 42030728..6b7dcf8d 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/shape/EmitterMeshFaceShapeTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/shape/EmitterMeshFaceShapeTreeNode.java @@ -2,6 +2,7 @@ import com.jme3.effect.shapes.EmitterMeshFaceShape; import com.ss.editor.Messages; +import com.ss.editor.annotation.FromAnyThread; import org.jetbrains.annotations.NotNull; /** @@ -11,19 +12,13 @@ */ public class EmitterMeshFaceShapeTreeNode extends EmitterShapeTreeNode { - /** - * Instantiates a new Emitter mesh face shape model node. - * - * @param element the element - * @param objectId the object id - */ public EmitterMeshFaceShapeTreeNode(@NotNull final EmitterMeshFaceShape element, final long objectId) { super(element, objectId); } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { return Messages.MODEL_FILE_EDITOR_NODE_PARTICLE_EMITTER_SHAPE_MESH_FACE; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/shape/EmitterMeshVertexShapeTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/shape/EmitterMeshVertexShapeTreeNode.java index fef08e51..f09a6265 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/shape/EmitterMeshVertexShapeTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/shape/EmitterMeshVertexShapeTreeNode.java @@ -2,6 +2,7 @@ import com.jme3.effect.shapes.EmitterMeshVertexShape; import com.ss.editor.Messages; +import com.ss.editor.annotation.FromAnyThread; import org.jetbrains.annotations.NotNull; /** @@ -11,19 +12,13 @@ */ public class EmitterMeshVertexShapeTreeNode extends EmitterShapeTreeNode { - /** - * Instantiates a new Emitter mesh vertex shape model node. - * - * @param element the element - * @param objectId the object id - */ public EmitterMeshVertexShapeTreeNode(@NotNull final EmitterMeshVertexShape element, final long objectId) { super(element, objectId); } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { return Messages.MODEL_FILE_EDITOR_NODE_PARTICLE_EMITTER_SHAPE_MESH_VERTEX; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/shape/EmitterPointShapeTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/shape/EmitterPointShapeTreeNode.java index 5b053c9c..f510aba8 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/shape/EmitterPointShapeTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/shape/EmitterPointShapeTreeNode.java @@ -2,6 +2,8 @@ import com.jme3.effect.shapes.EmitterPointShape; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; import javafx.scene.image.Image; import org.jetbrains.annotations.NotNull; @@ -14,25 +16,19 @@ */ public class EmitterPointShapeTreeNode extends EmitterShapeTreeNode { - /** - * Instantiates a new Emitter point shape model node. - * - * @param element the element - * @param objectId the object id - */ public EmitterPointShapeTreeNode(@NotNull final EmitterPointShape element, final long objectId) { super(element, objectId); } - @Nullable @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { return Icons.POINTS_16; } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { return Messages.MODEL_FILE_EDITOR_NODE_PARTICLE_EMITTER_SHAPE_POINT; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/shape/EmitterShapeTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/shape/EmitterShapeTreeNode.java index 38a41114..7bd82067 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/shape/EmitterShapeTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/shape/EmitterShapeTreeNode.java @@ -1,6 +1,8 @@ package com.ss.editor.ui.control.model.node.spatial.particle.emitter.shape; import com.jme3.effect.shapes.EmitterShape; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.tree.node.TreeNode; import javafx.scene.image.Image; @@ -14,25 +16,19 @@ */ public class EmitterShapeTreeNode extends TreeNode { - /** - * Instantiates a new Emitter shape model node. - * - * @param element the element - * @param objectId the object id - */ public EmitterShapeTreeNode(@NotNull final EmitterShape element, final long objectId) { super(element, objectId); } - @Nullable @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { return Icons.GEOMETRY_16; } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { final EmitterShape element = getElement(); return element.getClass().getSimpleName(); } diff --git a/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/shape/EmitterSphereShapeTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/shape/EmitterSphereShapeTreeNode.java index 71fa1cc5..dc5a5f47 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/shape/EmitterSphereShapeTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/shape/EmitterSphereShapeTreeNode.java @@ -2,6 +2,8 @@ import com.jme3.effect.shapes.EmitterSphereShape; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; import javafx.scene.image.Image; import org.jetbrains.annotations.NotNull; @@ -14,25 +16,19 @@ */ public class EmitterSphereShapeTreeNode extends EmitterShapeTreeNode { - /** - * Instantiates a new Emitter sphere shape model node. - * - * @param element the element - * @param objectId the object id - */ public EmitterSphereShapeTreeNode(@NotNull final EmitterSphereShape element, final long objectId) { super(element, objectId); } - @Nullable @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { return Icons.SPHERE_16; } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { return Messages.MODEL_FILE_EDITOR_NODE_PARTICLE_EMITTER_SHAPE_SPHERE; } } diff --git a/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/toneg0d/influencer/Toneg0DParticleInfluencerTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/toneg0d/influencer/Toneg0DParticleInfluencerTreeNode.java index 508756e7..234e5202 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/toneg0d/influencer/Toneg0DParticleInfluencerTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/toneg0d/influencer/Toneg0DParticleInfluencerTreeNode.java @@ -1,16 +1,16 @@ package com.ss.editor.ui.control.model.node.spatial.particle.emitter.toneg0d.influencer; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.model.tree.action.particle.emitter.toneg0d.influerencer.RemoveParticleInfluencerAction; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.editor.ui.control.tree.node.TreeNode; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - import javafx.collections.ObservableList; import javafx.scene.control.MenuItem; import javafx.scene.image.Image; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import tonegod.emitter.influencers.ParticleInfluencer; /** @@ -20,31 +20,26 @@ */ public class Toneg0DParticleInfluencerTreeNode extends TreeNode { - /** - * Instantiates a new Particle influencer model node. - * - * @param element the element - * @param objectId the object id - */ public Toneg0DParticleInfluencerTreeNode(@NotNull final ParticleInfluencer element, final long objectId) { super(element, objectId); } - @Nullable @Override - public Image getIcon() { + @FXThread + public @Nullable Image getIcon() { return Icons.INFLUENCER_16; } @Override + @FXThread public void fillContextMenu(@NotNull final NodeTree nodeTree, @NotNull final ObservableList items) { items.add(new RemoveParticleInfluencerAction(nodeTree, this)); super.fillContextMenu(nodeTree, items); } - @NotNull @Override - public String getName() { + @FromAnyThread + public @NotNull String getName() { final ParticleInfluencer element = getElement(); return element.getName(); } diff --git a/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/toneg0d/influencer/Toneg0DParticleInfluencersTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/toneg0d/influencer/Toneg0DParticleInfluencersTreeNode.java index e1b7d5ed..b2f4f16c 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/toneg0d/influencer/Toneg0DParticleInfluencersTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/spatial/particle/emitter/toneg0d/influencer/Toneg0DParticleInfluencersTreeNode.java @@ -3,6 +3,8 @@ import static com.ss.rlib.util.ClassUtils.getConstructor; import static com.ss.rlib.util.ClassUtils.newInstance; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.model.node.particles.Toneg0dParticleInfluencers; import com.ss.editor.ui.Icons; import com.ss.editor.ui.control.model.tree.ModelNodeTree; @@ -51,29 +53,24 @@ public class Toneg0DParticleInfluencersTreeNode extends TreeNode nodeTree, @NotNull final ObservableList items) { final Toneg0dParticleInfluencers element = getElement(); @@ -92,9 +89,10 @@ public void fillContextMenu(@NotNull final NodeTree nodeTree, @NotNull final super.fillContextMenu(nodeTree, items); } - @NotNull + @Override - public Array> getChildren(@NotNull final NodeTree nodeTree) { + @FXThread + public @NotNull Array> getChildren(@NotNull final NodeTree nodeTree) { final Array> result = ArrayFactory.newArray(TreeNode.class); final Toneg0dParticleInfluencers element = getElement(); final List influencers = element.getInfluencers(); @@ -103,6 +101,7 @@ public Array> getChildren(@NotNull final NodeTree nodeTree) { } @Override + @FXThread public boolean hasChildren(@NotNull final NodeTree nodeTree) { return nodeTree instanceof ModelNodeTree; } diff --git a/src/main/java/com/ss/editor/ui/control/tree/node/TreeNode.java b/src/main/java/com/ss/editor/ui/control/tree/node/TreeNode.java index afa83556..f67880a0 100644 --- a/src/main/java/com/ss/editor/ui/control/tree/node/TreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/tree/node/TreeNode.java @@ -1,6 +1,8 @@ package com.ss.editor.ui.control.tree.node; import com.ss.editor.Editor; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.model.UObject; import com.ss.editor.model.undo.editor.ChangeConsumer; import com.ss.editor.ui.control.tree.NodeTree; @@ -74,8 +76,8 @@ public TreeNode(@NotNull final T element, final long objectId) { * * @return the element of the {@link com.jme3.scene.Spatial}. */ - @NotNull - public T getElement() { + @FromAnyThread + public @NotNull T getElement() { return element; } @@ -84,6 +86,7 @@ public T getElement() { * * @return the name of this node. */ + @FromAnyThread public @NotNull String getName() { return "unknown name"; } @@ -93,6 +96,7 @@ public T getElement() { * * @param name the name. */ + @FXThread public void setName(@NotNull final String name) { } @@ -101,6 +105,7 @@ public void setName(@NotNull final String name) { * * @return true if need to save name. */ + @FromAnyThread public boolean isNeedToSaveName() { return false; } @@ -111,6 +116,7 @@ public boolean isNeedToSaveName() { * @param nodeTree the node tree * @return true of this node has any children. */ + @FXThread public boolean hasChildren(@NotNull final NodeTree nodeTree) { return false; } @@ -121,6 +127,7 @@ public boolean hasChildren(@NotNull final NodeTree nodeTree) { * @param nodeTree the node tree * @return the array of children of this node. */ + @FXThread public @NotNull Array> getChildren(@NotNull final NodeTree nodeTree) { return EMPTY_ARRAY; } @@ -130,8 +137,8 @@ public boolean hasChildren(@NotNull final NodeTree nodeTree) { * * @return the parent of this node. */ - @Nullable - public TreeNode getParent() { + @FXThread + public @Nullable TreeNode getParent() { return parent; } @@ -140,7 +147,8 @@ public TreeNode getParent() { * * @param parent the parent. */ - protected void setParent(final TreeNode parent) { + @FXThread + protected void setParent(@Nullable final TreeNode parent) { this.parent = parent; } @@ -149,6 +157,7 @@ protected void setParent(final TreeNode parent) { * * @return the icon of this node. */ + @FXThread public @Nullable Image getIcon() { return null; } @@ -159,6 +168,7 @@ protected void setParent(final TreeNode parent) { * @param nodeTree the node tree * @param items the items */ + @FXThread public void fillContextMenu(@NotNull final NodeTree nodeTree, @NotNull final ObservableList items) { } @@ -167,6 +177,7 @@ public void fillContextMenu(@NotNull final NodeTree nodeTree, @NotNull final * * @param child the child */ + @FXThread public void remove(@NotNull final TreeNode child) { } @@ -175,6 +186,7 @@ public void remove(@NotNull final TreeNode child) { * * @param child the child */ + @FXThread public void add(@NotNull final TreeNode child) { } @@ -184,6 +196,7 @@ public void add(@NotNull final TreeNode child) { * @param nodeTree the node tree * @param newName the new name */ + @FXThread public void changeName(@NotNull final NodeTree nodeTree, @NotNull final String newName) { } @@ -194,6 +207,7 @@ public void changeName(@NotNull final NodeTree nodeTree, @NotNull final Strin * @param isCopy true if need to copy the object. * @return true of this node can accept the child. */ + @FXThread public boolean canAccept(@NotNull final TreeNode child, final boolean isCopy) { return false; } @@ -205,6 +219,7 @@ public boolean canAccept(@NotNull final TreeNode child, final boolean isCopy) * @param object the object. * @param isCopy true if need to copy the object. */ + @FXThread public void accept(@NotNull final ChangeConsumer changeConsumer, @NotNull final Object object, final boolean isCopy) { } @@ -215,6 +230,7 @@ public void accept(@NotNull final ChangeConsumer changeConsumer, @NotNull final * @param dragboard the dragboard * @return true if this node can accept external resource. */ + @FXThread public boolean canAcceptExternal(@NotNull final Dragboard dragboard) { return false; } @@ -225,6 +241,7 @@ public boolean canAcceptExternal(@NotNull final Dragboard dragboard) { * @param dragboard the dragboard * @param consumer the consumer */ + @FXThread public void acceptExternal(@NotNull final Dragboard dragboard, @NotNull final ChangeConsumer consumer) { } @@ -233,6 +250,7 @@ public void acceptExternal(@NotNull final Dragboard dragboard, @NotNull final Ch * * @return true if this node supports moving. */ + @FXThread public boolean canMove() { return false; } @@ -242,6 +260,7 @@ public boolean canMove() { * * @return true if this node supports copying. */ + @FXThread public boolean canCopy() { return false; } @@ -251,6 +270,7 @@ public boolean canCopy() { * * @return true if this node supports name editing. */ + @FXThread public boolean canEditName() { return false; } @@ -260,6 +280,7 @@ public boolean canEditName() { * * @return true if you can remove this node. */ + @FXThread public boolean canRemove() { return true; } @@ -269,12 +290,13 @@ public boolean canRemove() { * * @return the new copy of this node. */ - @NotNull - public TreeNode copy() { + @FXThread + public @NotNull TreeNode copy() { throw new UnsupportedOperationException(); } @Override + @FromAnyThread public long getObjectId() { return objectId; } @@ -308,6 +330,7 @@ public int hashCode() { * * @param treeNode the model node. */ + @FXThread public void notifyChildAdded(@NotNull final TreeNode treeNode) { } @@ -316,6 +339,7 @@ public void notifyChildAdded(@NotNull final TreeNode treeNode) { * * @param treeNode the model node. */ + @FXThread public void notifyChildRemoved(@NotNull final TreeNode treeNode) { treeNode.setParent(null); } @@ -325,6 +349,7 @@ public void notifyChildRemoved(@NotNull final TreeNode treeNode) { * * @param treeNode the model node. */ + @FXThread public void notifyChildPreAdd(@NotNull final TreeNode treeNode) { treeNode.setParent(this); } @@ -334,6 +359,7 @@ public void notifyChildPreAdd(@NotNull final TreeNode treeNode) { * * @param treeNode the model node. */ + @FXThread public void notifyChildPreRemove(@NotNull final TreeNode treeNode) { } } From d0b92d1ba7815164d7ee853b03a763384258b74f Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Wed, 20 Sep 2017 17:31:46 +0300 Subject: [PATCH 33/50] updated editor API, fixed one bug. --- .../editor/BaseFileEditorWithRightTool.java | 137 ++++++++++++++++++ .../BaseFileEditorWithSplitRightTool.java | 53 +++++++ .../editor/area/EditorAreaComponent.java | 7 + .../impl/EditorWithEditorToolEditorState.java | 93 ++++++++++++ 4 files changed, 290 insertions(+) create mode 100644 src/main/java/com/ss/editor/plugin/api/editor/BaseFileEditorWithRightTool.java create mode 100644 src/main/java/com/ss/editor/plugin/api/editor/BaseFileEditorWithSplitRightTool.java create mode 100644 src/main/java/com/ss/editor/ui/component/editor/state/impl/EditorWithEditorToolEditorState.java diff --git a/src/main/java/com/ss/editor/plugin/api/editor/BaseFileEditorWithRightTool.java b/src/main/java/com/ss/editor/plugin/api/editor/BaseFileEditorWithRightTool.java new file mode 100644 index 00000000..94b9afb5 --- /dev/null +++ b/src/main/java/com/ss/editor/plugin/api/editor/BaseFileEditorWithRightTool.java @@ -0,0 +1,137 @@ +package com.ss.editor.plugin.api.editor; + +import static com.ss.rlib.util.ObjectUtils.notNull; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.ui.component.editor.state.impl.EditorWithEditorToolEditorState; +import com.ss.editor.ui.component.split.pane.EditorToolSplitPane; +import com.ss.editor.ui.component.tab.EditorToolComponent; +import com.ss.editor.ui.component.tab.ScrollableEditorToolComponent; +import com.ss.editor.ui.css.CSSClasses; +import com.ss.rlib.ui.util.FXUtils; +import javafx.scene.input.DragEvent; +import javafx.scene.layout.StackPane; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * The base implementation of a file editor without 3D part and with right tool panel. + * + * @author JavaSaBr + */ +public abstract class BaseFileEditorWithRightTool extends BaseFileEditor { + + /** + * The main split container. + */ + @Nullable + private EditorToolSplitPane mainSplitContainer; + + /** + * Editor tool component. + */ + @Nullable + private ScrollableEditorToolComponent editorToolComponent; + + /** + * The pane of editor area. + */ + @Nullable + private StackPane editorAreaPane; + + @Override + @FXThread + protected void createContent(@NotNull final StackPane root) { + createEditorAreaPane(); + + mainSplitContainer = new EditorToolSplitPane(JFX_APPLICATION.getScene(), root); + + editorToolComponent = new ScrollableEditorToolComponent(mainSplitContainer, 1); + editorToolComponent.prefHeightProperty().bind(root.heightProperty()); + + createToolComponents(editorToolComponent, root); + + editorToolComponent.addChangeListener((observable, oldValue, newValue) -> processChangeTool(oldValue, newValue)); + editorToolComponent.getSelectionModel().selectedIndexProperty().addListener((observable, oldValue, newValue) -> { + final S editorState = getEditorState(); + if (editorState != null) editorState.setOpenedTool(newValue.intValue()); + }); + + mainSplitContainer.initFor(editorToolComponent, getEditorAreaPane()); + + FXUtils.addToPane(mainSplitContainer, root); + FXUtils.addClassTo(mainSplitContainer, CSSClasses.FILE_EDITOR_MAIN_SPLIT_PANE); + } + + /** + * Create editor area pane. + */ + @FXThread + protected void createEditorAreaPane() { + + editorAreaPane = new StackPane(); + editorAreaPane.setOnDragOver(this::handleDragOverEvent); + editorAreaPane.setOnDragDropped(this::handleDragDroppedEvent); + + FXUtils.addClassTo(editorAreaPane, CSSClasses.FILE_EDITOR_EDITOR_AREA); + } + + /** + * Process change tool. + * + * @param oldValue the old value + * @param newValue the new value + */ + @FXThread + protected void processChangeTool(@Nullable final Number oldValue, @NotNull final Number newValue) { + } + + @Override + @FXThread + protected void loadState() { + super.loadState(); + + final S editorState = getEditorState(); + if (editorState == null) { + return; + } + + editorToolComponent.getSelectionModel().select(editorState.getOpenedTool()); + mainSplitContainer.updateFor(editorState); + } + + /** + * @return the pane of editor area. + */ + @FXThread + protected @NotNull StackPane getEditorAreaPane() { + return notNull(editorAreaPane); + } + + /** + * Create and add tool components to the container. + * + * @param container the tool container. + * @param root the root. + */ + @FXThread + protected void createToolComponents(@NotNull final EditorToolComponent container, @NotNull final StackPane root) { + } + + /** + * Handle drag over events. + * + * @param dragEvent the drag event. + */ + @FXThread + protected void handleDragOverEvent(@NotNull final DragEvent dragEvent) { + } + + /** + * Handle dropped events. + * + * @param dragEvent the drop event. + */ + @FXThread + protected void handleDragDroppedEvent(@NotNull final DragEvent dragEvent) { + } +} diff --git a/src/main/java/com/ss/editor/plugin/api/editor/BaseFileEditorWithSplitRightTool.java b/src/main/java/com/ss/editor/plugin/api/editor/BaseFileEditorWithSplitRightTool.java new file mode 100644 index 00000000..ffbc0422 --- /dev/null +++ b/src/main/java/com/ss/editor/plugin/api/editor/BaseFileEditorWithSplitRightTool.java @@ -0,0 +1,53 @@ +package com.ss.editor.plugin.api.editor; + +import com.ss.editor.annotation.FXThread; +import com.ss.editor.ui.component.editor.state.impl.EditorWithEditorToolEditorState; +import com.ss.editor.ui.css.CSSClasses; +import com.ss.rlib.ui.util.FXUtils; +import javafx.scene.Node; +import javafx.scene.control.SplitPane; +import javafx.scene.layout.Region; +import javafx.scene.layout.StackPane; +import org.jetbrains.annotations.NotNull; + +/** + * The base implementation of a file editor without 3D part and with right split tool panel. + * + * @author JavaSaBr + */ +public abstract class BaseFileEditorWithSplitRightTool extends + BaseFileEditorWithRightTool { + + /** + * Build split component. + * + * @param first the first component. + * @param second the second component. + * @param root the root. + * @return the result component. + */ + @FXThread + protected Region buildSplitComponent(@NotNull final Node first, @NotNull final Node second, + @NotNull final StackPane root) { + + final SplitPane splitPane = new SplitPane(first, second); + splitPane.prefHeightProperty().bind(root.heightProperty()); + splitPane.prefWidthProperty().bind(root.widthProperty()); + + root.heightProperty().addListener((observableValue, oldValue, newValue) -> calcVSplitSize(splitPane)); + + FXUtils.addClassTo(splitPane, CSSClasses.FILE_EDITOR_TOOL_SPLIT_PANE); + + return splitPane; + } + + /** + * Calc height of vertical split pane. + * + * @param splitPane the split pane + */ + @FXThread + protected void calcVSplitSize(@NotNull final SplitPane splitPane) { + splitPane.setDividerPosition(0, 0.3); + } +} diff --git a/src/main/java/com/ss/editor/ui/component/editor/area/EditorAreaComponent.java b/src/main/java/com/ss/editor/ui/component/editor/area/EditorAreaComponent.java index 854189db..47ad0381 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/area/EditorAreaComponent.java +++ b/src/main/java/com/ss/editor/ui/component/editor/area/EditorAreaComponent.java @@ -465,10 +465,17 @@ private void processOpenFileImpl(@NotNull final RequestedOpenFileEvent event, @N editor.openFile(file); } catch (final Throwable e) { EditorUtil.handleException(null, this, new Exception(e)); + + final Workspace workspace = WORKSPACE_MANAGER.getCurrentWorkspace(); + if (workspace != null) { + workspace.removeOpenedFile(file); + } + EXECUTOR_MANAGER.addFXTask(() -> { EditorUtil.decrementLoading(); resultEditor.notifyClosed(); }); + return; } finally { EDITOR.asyncUnlock(stamp); diff --git a/src/main/java/com/ss/editor/ui/component/editor/state/impl/EditorWithEditorToolEditorState.java b/src/main/java/com/ss/editor/ui/component/editor/state/impl/EditorWithEditorToolEditorState.java new file mode 100644 index 00000000..d500a54b --- /dev/null +++ b/src/main/java/com/ss/editor/ui/component/editor/state/impl/EditorWithEditorToolEditorState.java @@ -0,0 +1,93 @@ +package com.ss.editor.ui.component.editor.state.impl; + +import static java.lang.Math.abs; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.ui.component.editor.state.EditorToolConfig; + +/** + * The base implementation of state for {@link com.ss.editor.plugin.api.editor.BaseFileEditorWithRightTool}. + * + * @author JavaSaBr + */ +public class EditorWithEditorToolEditorState extends AbstractEditorState implements EditorToolConfig { + + /** + * The constant serialVersionUID. + */ + public static final long serialVersionUID = 1; + + /** + * The width of tool split panel. + */ + protected volatile int toolWidth; + + /** + * The flag of collapsing split panel. + */ + protected volatile boolean toolCollapsed; + + /** + * Opened editor tool. + */ + private volatile int openedTool; + + /** + * Instantiates a new Abstract editor state. + */ + public EditorWithEditorToolEditorState() { + this.toolWidth = 250; + this.toolCollapsed = false; + openedTool = 0; + } + + @Override + @FXThread + public int getToolWidth() { + return toolWidth; + } + + @Override + @FXThread + public void setToolWidth(final int toolWidth) { + final boolean changed = abs(getToolWidth() - toolWidth) > 3; + this.toolWidth = toolWidth; + if (changed) notifyChange(); + } + + @Override + @FXThread + public boolean isToolCollapsed() { + return toolCollapsed; + } + + @Override + @FXThread + public void setToolCollapsed(final boolean toolCollapsed) { + final boolean changed = isToolCollapsed() != toolCollapsed; + this.toolCollapsed = toolCollapsed; + if (changed) notifyChange(); + } + + /** + * Gets opened tool. + * + * @return the opened tool. + */ + public int getOpenedTool() { + return openedTool; + } + + /** + * Sets opened tool. + * + * @param openedTool the opened tool. + */ + public void setOpenedTool(final int openedTool) { + final boolean changed = getOpenedTool() != openedTool; + this.openedTool = openedTool; + final Runnable changeHandler = getChangeHandler(); + if (changed && changeHandler != null) { + changeHandler.run(); + } + } +} \ No newline at end of file From df0afee7782641641ddd4759dc2eb4733b5c5c4f Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Thu, 21 Sep 2017 07:36:01 +0300 Subject: [PATCH 34/50] updated editor API to works with key events. --- .../plugin/api/editor/BaseFileEditor.java | 24 ++++++++++++++++++- .../material/BaseMaterialEditor3DState.java | 15 ++++++++---- .../material/BaseMaterialFileEditor.java | 13 ++++------ .../scene/AbstractSceneEditor3DState.java | 15 ++++++++---- .../editor/impl/AbstractFileEditor.java | 15 +++++++----- .../impl/scene/AbstractSceneFileEditor.java | 5 ++-- 6 files changed, 61 insertions(+), 26 deletions(-) diff --git a/src/main/java/com/ss/editor/plugin/api/editor/BaseFileEditor.java b/src/main/java/com/ss/editor/plugin/api/editor/BaseFileEditor.java index bee90726..89c840b0 100644 --- a/src/main/java/com/ss/editor/plugin/api/editor/BaseFileEditor.java +++ b/src/main/java/com/ss/editor/plugin/api/editor/BaseFileEditor.java @@ -12,6 +12,7 @@ import com.ss.editor.ui.component.editor.impl.AbstractFileEditor; import com.ss.editor.ui.component.editor.state.EditorState; import javafx.event.Event; +import javafx.scene.input.KeyCode; import javafx.scene.layout.BorderPane; import javafx.scene.layout.StackPane; import org.jetbrains.annotations.NotNull; @@ -63,6 +64,7 @@ protected BaseFileEditor() { * * @return the editor operation control. */ + @FXThread protected @NotNull EditorOperationControl createOperationControl() { return new EditorOperationControl(this); } @@ -73,6 +75,26 @@ public void execute(@NotNull final EditorOperation operation) { operationControl.execute(operation); } + @FXThread + @Override + protected boolean handleKeyActionImpl(@NotNull final KeyCode keyCode, final boolean isPressed, + final boolean isControlDown, final boolean isShiftDown, + final boolean isButtonMiddleDown) { + + if (isPressed && isControlDown && keyCode == KeyCode.Z) { + undo(); + return true; + } else if (isPressed && isControlDown && isShiftDown && keyCode == KeyCode.Z) { + redo(); + return true; + } else if (isPressed && isControlDown && keyCode == KeyCode.Y) { + redo(); + return true; + } + + return super.handleKeyActionImpl(keyCode, isPressed, isControlDown, isShiftDown, isButtonMiddleDown); + } + @Override @FXThread public void incrementChange() { @@ -198,8 +220,8 @@ protected boolean isIgnoreListeners() { return editorState; } - @FXThread @Override + @FXThread public boolean isInside(final double sceneX, final double sceneY, @NotNull final Class eventType) { return false; } diff --git a/src/main/java/com/ss/editor/plugin/api/editor/material/BaseMaterialEditor3DState.java b/src/main/java/com/ss/editor/plugin/api/editor/material/BaseMaterialEditor3DState.java index 551a1b13..2db742b5 100644 --- a/src/main/java/com/ss/editor/plugin/api/editor/material/BaseMaterialEditor3DState.java +++ b/src/main/java/com/ss/editor/plugin/api/editor/material/BaseMaterialEditor3DState.java @@ -126,10 +126,17 @@ protected void registerActionHandlers(@NotNull final ObjectDictionary fileEditor.handleKeyAction(KeyCode.S, isPressed, isControlDown(), isButtonMiddleDown())); - actionHandlers.put(KEY_C, (isPressed, tpf) -> fileEditor.handleKeyAction(KeyCode.C, isPressed, isControlDown(), isButtonMiddleDown())); - actionHandlers.put(KEY_P, (isPressed, tpf) -> fileEditor.handleKeyAction(KeyCode.P, isPressed, isControlDown(), isButtonMiddleDown())); - actionHandlers.put(KEY_L, (isPressed, tpf) -> fileEditor.handleKeyAction(KeyCode.L, isPressed, isControlDown(), isButtonMiddleDown())); + actionHandlers.put(KEY_S, (isPressed, tpf) -> + fileEditor.handleKeyAction(KeyCode.S, isPressed, isControlDown(), isShiftDown(), isButtonMiddleDown())); + + actionHandlers.put(KEY_C, (isPressed, tpf) -> + fileEditor.handleKeyAction(KeyCode.C, isPressed, isControlDown(), isShiftDown(), isButtonMiddleDown())); + + actionHandlers.put(KEY_P, (isPressed, tpf) -> + fileEditor.handleKeyAction(KeyCode.P, isPressed, isControlDown(), isShiftDown(), isButtonMiddleDown())); + + actionHandlers.put(KEY_L, (isPressed, tpf) -> + fileEditor.handleKeyAction(KeyCode.L, isPressed, isControlDown(), isShiftDown(), isButtonMiddleDown())); } @Override diff --git a/src/main/java/com/ss/editor/plugin/api/editor/material/BaseMaterialFileEditor.java b/src/main/java/com/ss/editor/plugin/api/editor/material/BaseMaterialFileEditor.java index c3739712..484e5d4e 100644 --- a/src/main/java/com/ss/editor/plugin/api/editor/material/BaseMaterialFileEditor.java +++ b/src/main/java/com/ss/editor/plugin/api/editor/material/BaseMaterialFileEditor.java @@ -112,15 +112,10 @@ protected C getChangeConsumer() { @Override @FXThread protected boolean handleKeyActionImpl(@NotNull final KeyCode keyCode, final boolean isPressed, - final boolean isControlDown, final boolean isButtonMiddleDown) { + final boolean isControlDown, final boolean isShiftDown, + final boolean isButtonMiddleDown) { - if (isPressed && isControlDown && keyCode == KeyCode.Z) { - undo(); - return true; - } else if (isPressed && isControlDown && keyCode == KeyCode.Y) { - redo(); - return true; - } else if (isPressed && keyCode == KeyCode.C && !isControlDown && !isButtonMiddleDown) { + if (isPressed && keyCode == KeyCode.C && !isControlDown && !isButtonMiddleDown) { final ToggleButton cubeButton = getCubeButton(); cubeButton.setSelected(true); return true; @@ -138,7 +133,7 @@ protected boolean handleKeyActionImpl(@NotNull final KeyCode keyCode, final bool return true; } - return super.handleKeyActionImpl(keyCode, isPressed, isControlDown, isButtonMiddleDown); + return super.handleKeyActionImpl(keyCode, isPressed, isControlDown, isShiftDown, isButtonMiddleDown); } @Override diff --git a/src/main/java/com/ss/editor/state/editor/impl/scene/AbstractSceneEditor3DState.java b/src/main/java/com/ss/editor/state/editor/impl/scene/AbstractSceneEditor3DState.java index 20083c60..16de3bd9 100644 --- a/src/main/java/com/ss/editor/state/editor/impl/scene/AbstractSceneEditor3DState.java +++ b/src/main/java/com/ss/editor/state/editor/impl/scene/AbstractSceneEditor3DState.java @@ -355,10 +355,17 @@ protected void registerActionHandlers(@NotNull final ObjectDictionary fileEditor.handleKeyAction(KeyCode.S, isPressed, isControlDown(), isButtonMiddleDown())); - actionHandlers.put(KEY_G, (isPressed, tpf) -> fileEditor.handleKeyAction(KeyCode.G, isPressed, isControlDown(), isButtonMiddleDown())); - actionHandlers.put(KEY_R, (isPressed, tpf) -> fileEditor.handleKeyAction(KeyCode.R, isPressed, isControlDown(), isButtonMiddleDown())); - actionHandlers.put(KEY_DEL, (isPressed, tpf) -> fileEditor.handleKeyAction(KeyCode.DELETE, isPressed, isControlDown(), isButtonMiddleDown())); + actionHandlers.put(KEY_S, (isPressed, tpf) -> + fileEditor.handleKeyAction(KeyCode.S, isPressed, isControlDown(), isShiftDown(), isButtonMiddleDown())); + + actionHandlers.put(KEY_G, (isPressed, tpf) -> + fileEditor.handleKeyAction(KeyCode.G, isPressed, isControlDown(), isShiftDown(), isButtonMiddleDown())); + + actionHandlers.put(KEY_R, (isPressed, tpf) -> + fileEditor.handleKeyAction(KeyCode.R, isPressed, isControlDown(), isShiftDown(), isButtonMiddleDown())); + + actionHandlers.put(KEY_DEL, (isPressed, tpf) -> + fileEditor.handleKeyAction(KeyCode.DELETE, isPressed, isControlDown(), isShiftDown(), isButtonMiddleDown())); } @Override diff --git a/src/main/java/com/ss/editor/ui/component/editor/impl/AbstractFileEditor.java b/src/main/java/com/ss/editor/ui/component/editor/impl/AbstractFileEditor.java index 115d685e..0cba93f2 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/impl/AbstractFileEditor.java +++ b/src/main/java/com/ss/editor/ui/component/editor/impl/AbstractFileEditor.java @@ -266,7 +266,7 @@ protected void processKeyReleased(@NotNull final KeyEvent event) { final KeyCode code = event.getCode(); - if (handleKeyActionImpl(code, true, event.isControlDown(), false)) { + if (handleKeyActionImpl(code, true, event.isControlDown(), event.isShiftDown(), false)) { event.consume(); } } @@ -277,12 +277,13 @@ protected void processKeyReleased(@NotNull final KeyEvent event) { * @param keyCode the key code. * @param isPressed true if key is pressed. * @param isControlDown true if control is down. + * @param isShiftDown true if shift is down. * @param isButtonMiddleDown true if mouse middle button is pressed. */ @FromAnyThread public void handleKeyAction(@NotNull final KeyCode keyCode, final boolean isPressed, final boolean isControlDown, - final boolean isButtonMiddleDown) { - EXECUTOR_MANAGER.addFXTask(() -> handleKeyActionImpl(keyCode, isPressed, isControlDown, isButtonMiddleDown)); + final boolean isShiftDown, final boolean isButtonMiddleDown) { + EXECUTOR_MANAGER.addFXTask(() -> handleKeyActionImpl(keyCode, isPressed, isControlDown, isShiftDown, isButtonMiddleDown)); } /** @@ -291,12 +292,14 @@ public void handleKeyAction(@NotNull final KeyCode keyCode, final boolean isPres * @param keyCode the key code. * @param isPressed true if key is pressed. * @param isControlDown true if control is down. + * @param isShiftDown true if shift is down. * @param isButtonMiddleDown true if mouse middle button is pressed. - * @return true if can consume an event. + * @return true if need to consume an event. */ @FXThread protected boolean handleKeyActionImpl(@NotNull final KeyCode keyCode, final boolean isPressed, - final boolean isControlDown, final boolean isButtonMiddleDown) { + final boolean isControlDown, final boolean isShiftDown, + final boolean isButtonMiddleDown) { return false; } @@ -312,7 +315,7 @@ protected void processKeyPressed(@NotNull final KeyEvent event) { if (code == KeyCode.S && event.isControlDown() && isDirty()) { save(); - } else if (handleKeyActionImpl(code, true, event.isControlDown(), false)) { + } else if (handleKeyActionImpl(code, true, event.isControlDown(), event.isShiftDown(), false)) { event.consume(); } } diff --git a/src/main/java/com/ss/editor/ui/component/editor/impl/scene/AbstractSceneFileEditor.java b/src/main/java/com/ss/editor/ui/component/editor/impl/scene/AbstractSceneFileEditor.java index 39212e0b..77add438 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/impl/scene/AbstractSceneFileEditor.java +++ b/src/main/java/com/ss/editor/ui/component/editor/impl/scene/AbstractSceneFileEditor.java @@ -459,7 +459,8 @@ protected void loadState() { @Override @FXThread protected boolean handleKeyActionImpl(@NotNull final KeyCode keyCode, final boolean isPressed, - final boolean isControlDown, final boolean isButtonMiddleDown) { + final boolean isControlDown, final boolean isShiftDown, + final boolean isButtonMiddleDown) { final MA editor3DState = getEditor3DState(); if (editor3DState.isCameraMoving()) { @@ -508,7 +509,7 @@ protected boolean handleKeyActionImpl(@NotNull final KeyCode keyCode, final bool return true; } - return super.handleKeyActionImpl(keyCode, isPressed, isControlDown, isButtonMiddleDown); + return super.handleKeyActionImpl(keyCode, isPressed, isControlDown, isShiftDown, isButtonMiddleDown); } /** From 721cdb0a18848512aa7b7abe571071b9779f5781 Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Thu, 21 Sep 2017 09:57:10 +0300 Subject: [PATCH 35/50] fixed key event listening. --- .../ss/editor/ui/component/editor/impl/AbstractFileEditor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/ss/editor/ui/component/editor/impl/AbstractFileEditor.java b/src/main/java/com/ss/editor/ui/component/editor/impl/AbstractFileEditor.java index 0cba93f2..a531441c 100644 --- a/src/main/java/com/ss/editor/ui/component/editor/impl/AbstractFileEditor.java +++ b/src/main/java/com/ss/editor/ui/component/editor/impl/AbstractFileEditor.java @@ -266,7 +266,7 @@ protected void processKeyReleased(@NotNull final KeyEvent event) { final KeyCode code = event.getCode(); - if (handleKeyActionImpl(code, true, event.isControlDown(), event.isShiftDown(), false)) { + if (handleKeyActionImpl(code, false, event.isControlDown(), event.isShiftDown(), false)) { event.consume(); } } From e16136df7657f1b66391d2ad67d5b9d76c7275d3 Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Thu, 21 Sep 2017 11:20:06 +0300 Subject: [PATCH 36/50] added new property control. --- .../editor/extension/loader/SceneLoader.java | 134 ------------------ .../api/property/PropertyDefinition.java | 36 ++++- .../FileAssetResourcePropertyControl.java | 71 ++++++++++ .../GeometryAssetResourcePropertyControl.java | 4 +- .../control/PropertyEditorControl.java | 3 + .../control/PropertyEditorControlFactory.java | 2 + .../SpatialAssetResourcePropertyControl.java | 4 +- 7 files changed, 114 insertions(+), 140 deletions(-) delete mode 100644 src/main/java/com/ss/editor/extension/loader/SceneLoader.java create mode 100644 src/main/java/com/ss/editor/plugin/api/property/control/FileAssetResourcePropertyControl.java diff --git a/src/main/java/com/ss/editor/extension/loader/SceneLoader.java b/src/main/java/com/ss/editor/extension/loader/SceneLoader.java deleted file mode 100644 index 1464cc7c..00000000 --- a/src/main/java/com/ss/editor/extension/loader/SceneLoader.java +++ /dev/null @@ -1,134 +0,0 @@ -package com.ss.editor.extension.loader; - -import static java.util.Objects.requireNonNull; -import com.jme3.app.Application; -import com.jme3.app.state.AppStateManager; -import com.jme3.asset.AssetInfo; -import com.jme3.asset.AssetManager; -import com.jme3.export.InputCapsule; -import com.jme3.export.JmeImporter; -import com.jme3.export.Savable; -import com.jme3.export.binary.BinaryImporter; -import com.jme3.post.FilterPostProcessor; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.IOException; -import java.util.ArrayDeque; -import java.util.Deque; - -/** - * The implementation of jME Importer to load scenes. - * - * @author JavaSaBr - */ -public class SceneLoader implements JmeImporter { - - /** - * The application. - */ - @Nullable - private static Application application; - - /** - * The filter post processor. - */ - @Nullable - private static FilterPostProcessor processor; - - /** - * Install a scene loader to the asset manager. - * - * @param application the application. - */ - public static void install(@NotNull final Application application) { - install(application, null); - } - - /** - * Install a scene loader to the asset manager. - * - * @param application the application. - * @param processor the processor. - */ - public static void install(@NotNull final Application application, @Nullable final FilterPostProcessor processor) { - final AssetManager assetManager = application.getAssetManager(); - assetManager.unregisterLoader(BinaryImporter.class); - assetManager.registerLoader(SceneLoader.class, "j3o", "j3f", "j3s"); - SceneLoader.application = application; - SceneLoader.processor = processor; - } - - public static @NotNull AssetManager tryToGetAssetManager() { - return requireNonNull(application).getAssetManager(); - } - - public static @NotNull AppStateManager tryToGetStateManager() { - return requireNonNull(application).getStateManager(); - } - - public static @Nullable FilterPostProcessor tryToGetPostProcessor() { - return processor; - } - - /** - * The thread local importers. - */ - @NotNull - private final ThreadLocal> threadLocalImporters; - - /** - * The current importer. - */ - @NotNull - private final ThreadLocal currentImporter; - - public SceneLoader() { - currentImporter = new ThreadLocal<>(); - threadLocalImporters = new ThreadLocal>() { - - @Override - protected Deque initialValue() { - return new ArrayDeque<>(); - } - }; - } - - @Override - public InputCapsule getCapsule(final Savable id) { - final BinaryImporter importer = currentImporter.get(); - return importer.getCapsule(id); - } - - @Override - public AssetManager getAssetManager() { - final BinaryImporter importer = currentImporter.get(); - return importer.getAssetManager(); - } - - @Override - public int getFormatVersion() { - final BinaryImporter importer = currentImporter.get(); - return importer.getFormatVersion(); - } - - @Override - public Object load(@NotNull final AssetInfo assetInfo) throws IOException { - - final Deque importers = threadLocalImporters.get(); - BinaryImporter importer = importers.pollLast(); - - if (importer == null) { - importer = new BinaryImporter(); - } - - final BinaryImporter prev = currentImporter.get(); - currentImporter.set(importer); - try { - return importer.load(assetInfo); - } finally { - importers.addLast(importer); - currentImporter.set(prev); - } - } -} diff --git a/src/main/java/com/ss/editor/plugin/api/property/PropertyDefinition.java b/src/main/java/com/ss/editor/plugin/api/property/PropertyDefinition.java index 9d862837..f135e437 100644 --- a/src/main/java/com/ss/editor/plugin/api/property/PropertyDefinition.java +++ b/src/main/java/com/ss/editor/plugin/api/property/PropertyDefinition.java @@ -47,6 +47,12 @@ public final class PropertyDefinition { @NotNull private final Array options; + /** + * The file extension to filter files/resources. + */ + @Nullable + private final String extension; + /** * The min value. */ @@ -66,6 +72,20 @@ public PropertyDefinition(@NotNull final EditablePropertyType propertyType, @Not this.max = Float.NaN; this.min = Float.NaN; this.options = EMPTY_OPTIONS; + this.extension = null; + } + + public PropertyDefinition(@NotNull final EditablePropertyType propertyType, @NotNull final String name, + @NotNull final String id, @Nullable final Object defaultValue, + @Nullable final String extension) { + this.propertyType = propertyType; + this.name = name; + this.id = id; + this.defaultValue = defaultValue; + this.max = Float.NaN; + this.min = Float.NaN; + this.options = EMPTY_OPTIONS; + this.extension = extension; } public PropertyDefinition(@NotNull final EditablePropertyType propertyType, @NotNull final String name, @@ -78,6 +98,7 @@ public PropertyDefinition(@NotNull final EditablePropertyType propertyType, @Not this.options = options; this.max = Float.NaN; this.min = Float.NaN; + this.extension = null; } public PropertyDefinition(@NotNull final EditablePropertyType propertyType, @NotNull final String name, @@ -90,6 +111,7 @@ public PropertyDefinition(@NotNull final EditablePropertyType propertyType, @Not this.min = min; this.max = max; this.options = EMPTY_OPTIONS; + this.extension = null; } /** @@ -148,10 +170,20 @@ public float getMin() { return options; } + /** + * Get the file extension to filter files/resources. + * + * @return the file extension to filter files/resources. + */ + @FromAnyThread + public @Nullable String getExtension() { + return extension; + } + @Override public String toString() { return "PropertyDefinition{" + "propertyType=" + propertyType + ", name='" + name + '\'' + ", id='" + id + - '\'' + ", defaultValue=" + defaultValue + ", options=" + options + ", min=" + min + ", max=" + max + - '}'; + '\'' + ", defaultValue=" + defaultValue + ", options=" + options + ", extension='" + extension + '\'' + + ", min=" + min + ", max=" + max + '}'; } } diff --git a/src/main/java/com/ss/editor/plugin/api/property/control/FileAssetResourcePropertyControl.java b/src/main/java/com/ss/editor/plugin/api/property/control/FileAssetResourcePropertyControl.java new file mode 100644 index 00000000..113882a8 --- /dev/null +++ b/src/main/java/com/ss/editor/plugin/api/property/control/FileAssetResourcePropertyControl.java @@ -0,0 +1,71 @@ +package com.ss.editor.plugin.api.property.control; + +import static com.ss.editor.util.EditorUtil.getAssetFile; +import static com.ss.editor.util.EditorUtil.toAssetPath; +import static com.ss.rlib.util.ObjectUtils.notNull; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; +import com.ss.editor.plugin.api.property.PropertyDefinition; +import com.ss.editor.ui.component.asset.tree.context.menu.action.NewFileAction; +import com.ss.rlib.util.VarTable; +import com.ss.rlib.util.array.Array; +import com.ss.rlib.util.array.ArrayFactory; +import javafx.scene.control.Label; +import org.jetbrains.annotations.NotNull; + +import java.nio.file.Path; +import java.util.function.Predicate; + +/** + * The control to edit file values from asset folder. + * + * @author JavaSaBr + */ +public class FileAssetResourcePropertyControl extends AssetResourcePropertyEditorControl { + + @NotNull + private static final Predicate> ACTION_TESTER = type -> type == NewFileAction.class; + + /** + * The list of target extensions. + */ + @NotNull + private final Array extensions; + + public FileAssetResourcePropertyControl(@NotNull final VarTable vars, @NotNull final PropertyDefinition definition, + @NotNull final Runnable validationCallback) { + super(vars, definition, validationCallback); + this.extensions = ArrayFactory.asArray(notNull(definition.getExtension())); + } + + @Override + @FromAnyThread + public @NotNull Predicate> getActionTester() { + return ACTION_TESTER; + } + + @Override + @FromAnyThread + protected @NotNull Array getExtensions() { + return extensions; + } + + @Override + @FXThread + protected void processSelect(@NotNull final Path file) { + setPropertyValue(notNull(getAssetFile(file))); + super.processSelect(file); + } + + @Override + @FXThread + protected void reload() { + + final Path file = getPropertyValue(); + + final Label resourceLabel = getResourceLabel(); + resourceLabel.setText(file == null ? NOT_SELECTED : toAssetPath(file)); + + super.reload(); + } +} diff --git a/src/main/java/com/ss/editor/plugin/api/property/control/GeometryAssetResourcePropertyControl.java b/src/main/java/com/ss/editor/plugin/api/property/control/GeometryAssetResourcePropertyControl.java index d881d979..f5ad8ff8 100644 --- a/src/main/java/com/ss/editor/plugin/api/property/control/GeometryAssetResourcePropertyControl.java +++ b/src/main/java/com/ss/editor/plugin/api/property/control/GeometryAssetResourcePropertyControl.java @@ -19,8 +19,8 @@ public class GeometryAssetResourcePropertyControl extends SpatialAssetResourcePropertyControl { public GeometryAssetResourcePropertyControl(@NotNull final VarTable vars, - @NotNull final PropertyDefinition definition, - @NotNull final Runnable validationCallback) { + @NotNull final PropertyDefinition definition, + @NotNull final Runnable validationCallback) { super(vars, definition, validationCallback); } diff --git a/src/main/java/com/ss/editor/plugin/api/property/control/PropertyEditorControl.java b/src/main/java/com/ss/editor/plugin/api/property/control/PropertyEditorControl.java index fecd8771..5dc6fc9e 100644 --- a/src/main/java/com/ss/editor/plugin/api/property/control/PropertyEditorControl.java +++ b/src/main/java/com/ss/editor/plugin/api/property/control/PropertyEditorControl.java @@ -32,6 +32,9 @@ public class PropertyEditorControl extends HBox { */ public static final double DEFAULT_FIELD_W_PERCENT = AbstractSimpleEditorDialog.DEFAULT_FIELD_W_PERCENT; + /** + * The editor. + */ @NotNull protected static final Editor EDITOR = Editor.getInstance(); diff --git a/src/main/java/com/ss/editor/plugin/api/property/control/PropertyEditorControlFactory.java b/src/main/java/com/ss/editor/plugin/api/property/control/PropertyEditorControlFactory.java index 1af84a34..323bcd7d 100644 --- a/src/main/java/com/ss/editor/plugin/api/property/control/PropertyEditorControlFactory.java +++ b/src/main/java/com/ss/editor/plugin/api/property/control/PropertyEditorControlFactory.java @@ -48,6 +48,8 @@ public class PropertyEditorControlFactory { return new StringPropertyEditorControl(vars, definition, validation); case GEOMETRY_FROM_ASSET_FOLDER: return new GeometryAssetResourcePropertyControl(vars, definition, validation); + case FILE_FROM_ASSET_FOLDER: + return new FileAssetResourcePropertyControl(vars, definition, validation); case STRING_FROM_LIST: return new StringFromListPropertyEditorControl(vars, definition, validation, definition.getOptions()); case AWT_FONT: diff --git a/src/main/java/com/ss/editor/plugin/api/property/control/SpatialAssetResourcePropertyControl.java b/src/main/java/com/ss/editor/plugin/api/property/control/SpatialAssetResourcePropertyControl.java index 19da7297..2c2f641b 100644 --- a/src/main/java/com/ss/editor/plugin/api/property/control/SpatialAssetResourcePropertyControl.java +++ b/src/main/java/com/ss/editor/plugin/api/property/control/SpatialAssetResourcePropertyControl.java @@ -37,8 +37,8 @@ public class SpatialAssetResourcePropertyControl extends Asse } public SpatialAssetResourcePropertyControl(@NotNull final VarTable vars, - @NotNull final PropertyDefinition definition, - @NotNull final Runnable validationCallback) { + @NotNull final PropertyDefinition definition, + @NotNull final Runnable validationCallback) { super(vars, definition, validationCallback); } From 19e7d0c6efec64a7fb1122e0f51f9dbdd064e60b Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Thu, 21 Sep 2017 11:20:27 +0300 Subject: [PATCH 37/50] updated version of extension library. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 81befeca..7b672157 100644 --- a/build.gradle +++ b/build.gradle @@ -125,7 +125,7 @@ dependencies { compile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.3' // extensions - compile ('com.github.JavaSaBr:jme3-spaceshift-extension:1.6.0') { + compile ('com.github.JavaSaBr:jme3-spaceshift-extension:1.7.0') { exclude group: 'org.jmonkeyengine' } compile ('com.github.JavaSaBr:tonegodemitter:2.4.0') { From cf6a90c6f6a307ef024e610b393ef2419ea074e1 Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Fri, 22 Sep 2017 09:02:29 +0300 Subject: [PATCH 38/50] updated API of code component. --- .../editor/ui/control/code/BaseCodeArea.java | 134 +++++++++++++++++- .../editor/ui/control/code/GLSLCodeArea.java | 122 +++++++--------- .../code/MaterialDefinitionCodeArea.java | 68 +-------- .../java/com/ss/editor/util/GLSLType.java | 73 ++++++++++ 4 files changed, 262 insertions(+), 135 deletions(-) create mode 100644 src/main/java/com/ss/editor/util/GLSLType.java diff --git a/src/main/java/com/ss/editor/ui/control/code/BaseCodeArea.java b/src/main/java/com/ss/editor/ui/control/code/BaseCodeArea.java index d2a28539..7b5d6a2a 100644 --- a/src/main/java/com/ss/editor/ui/control/code/BaseCodeArea.java +++ b/src/main/java/com/ss/editor/ui/control/code/BaseCodeArea.java @@ -1,12 +1,17 @@ package com.ss.editor.ui.control.code; +import static java.util.Collections.singleton; import com.ss.editor.annotation.FXThread; +import com.ss.rlib.util.StringUtils; import org.fxmisc.richtext.CodeArea; import org.fxmisc.richtext.model.StyleSpans; +import org.fxmisc.richtext.model.StyleSpansBuilder; import org.fxmisc.undo.UndoManager; import org.jetbrains.annotations.NotNull; import java.util.Collection; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * The base implementation of code area control for this editor. @@ -15,11 +20,104 @@ */ public class BaseCodeArea extends CodeArea { + @NotNull + protected static final String CLASS_KEYWORD = "KEYWORD"; + + @NotNull + protected static final String CLASS_VALUE_TYPE = "VALUETYPE"; + + @NotNull + protected static final String CLASS_VALUE_VALUE = "VALUEVALUE"; + + @NotNull + protected static final String CLASS_PAREN = "PAREN"; + + @NotNull + protected static final String CLASS_BRACE = "BRACE"; + + @NotNull + protected static final String CLASS_BRACKET = "BRACKET"; + + @NotNull + protected static final String CLASS_SEMICOLON = "SEMICOLON"; + + @NotNull + protected static final String CLASS_STRING = "STRING"; + + @NotNull + protected static final String CLASS_COMMENT = "COMMENT"; + + @NotNull + protected static final String[][] AVAILABLE_CLASSES = { + {CLASS_KEYWORD, "keyword"}, + {CLASS_VALUE_TYPE, "value-type"}, + {CLASS_VALUE_VALUE, "value-value"}, + {CLASS_PAREN, "paren"}, + {CLASS_BRACE, "brace"}, + {CLASS_BRACKET, "bracket"}, + {CLASS_SEMICOLON, "semicolon"}, + {CLASS_STRING, "string"}, + {CLASS_COMMENT, "comment"}, + }; + + protected static final String PAREN_PATTERN = "\\(|\\)"; + protected static final String BRACE_PATTERN = "\\{|\\}"; + protected static final String BRACKET_PATTERN = "\\[|\\]"; + protected static final String SEMICOLON_PATTERN = "\\;"; + protected static final String STRING_PATTERN = "\"([^\"\\\\]|\\\\.)*\""; + protected static final String COMMENT_PATTERN = "//[^\n]*" + "|" + "/\\*(.|\\R)*?\\*/"; + public BaseCodeArea() { richChanges().filter(ch -> !ch.getInserted().equals(ch.getRemoved())) .subscribe(change -> setStyleSpans(0, calculateStyleSpans(getText()))); } + /** + * Calculate highlighting for the code. + * + * @param pattern the pattern. + * @param text the text. + * @return the highlight styles. + */ + @FXThread + protected @NotNull StyleSpans> computeHighlighting(@NotNull final Pattern pattern, + @NotNull final String text) { + + final Matcher matcher = pattern.matcher(text); + final StyleSpansBuilder> spansBuilder = new StyleSpansBuilder<>(); + + int lastKwEnd = 0; + + while (matcher.find()) { + + String styleClass = null; + + for (final String[] availableClass : AVAILABLE_CLASSES) { + + try { + styleClass = matcher.group(availableClass[0]) != null ? availableClass[1] : null; + } catch (final IllegalArgumentException e) { + continue; + } + + if(styleClass != null) { + break; + } + } + + assert styleClass != null; + + spansBuilder.add(singleton("plain-code"), matcher.start() - lastKwEnd); + spansBuilder.add(singleton(styleClass), matcher.end() - matcher.start()); + + lastKwEnd = matcher.end(); + } + + spansBuilder.add(singleton("plain-code"), text.length() - lastKwEnd); + + return spansBuilder.create(); + } + /** * Gets style spans. * @@ -31,6 +129,11 @@ public BaseCodeArea() { throw new RuntimeException("unsupported"); } + /** + * Load the content. + * + * @param content the content. + */ @FXThread public void loadContent(@NotNull final String content) { appendText(content); @@ -39,9 +142,38 @@ public void loadContent(@NotNull final String content) { undoManager.forgetHistory(); } + /** + * Reload the content. + * + * @param content the content. + */ @FXThread public void reloadContent(@NotNull final String content) { + reloadContent(content, false); + } + + /** + * Reload the content. + * + * @param content the content. + * @param clearHistory true if need to clear history. + */ + @FXThread + public void reloadContent(@NotNull final String content, final boolean clearHistory) { + final String currentContent = getText(); - replaceText(0, currentContent.length(), content); + + if (!StringUtils.equals(currentContent, content)) { + if (content.isEmpty()) { + deleteText(0, currentContent.length()); + } else { + replaceText(0, currentContent.length(), content); + } + } + + if (clearHistory) { + final UndoManager undoManager = getUndoManager(); + undoManager.forgetHistory(); + } } } diff --git a/src/main/java/com/ss/editor/ui/control/code/GLSLCodeArea.java b/src/main/java/com/ss/editor/ui/control/code/GLSLCodeArea.java index a64c564e..f7e05540 100644 --- a/src/main/java/com/ss/editor/ui/control/code/GLSLCodeArea.java +++ b/src/main/java/com/ss/editor/ui/control/code/GLSLCodeArea.java @@ -1,13 +1,15 @@ package com.ss.editor.ui.control.code; -import static java.util.Collections.singleton; import com.ss.editor.annotation.FXThread; +import com.ss.editor.util.GLSLType; import org.fxmisc.richtext.model.StyleSpans; -import org.fxmisc.richtext.model.StyleSpansBuilder; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; -import java.util.regex.Matcher; +import java.util.List; import java.util.regex.Pattern; /** @@ -30,87 +32,73 @@ public class GLSLCodeArea extends BaseCodeArea { }; @NotNull - private static final String[] VALUE_TYPES = { - "float", "double", "int", "bool", "mat2", "mat3", "mat4", "uint", "uvec2", "uvec3", "uvec4", - "sampler1D", "sampler2D", "sampler3D", "samplerCube", "vec2", "vec3", "vec4" - }; + private static final String[] VALUE_TYPES; + + static { + VALUE_TYPES = Arrays.stream(GLSLType.VALUES) + .map(GLSLType::getRawType) + .toArray(String[]::new); + } private static final String KEYWORD_PATTERN = "\\b(" + String.join("|", KEYWORDS) + ")\\b"; private static final String VALUE_TYPE_PATTERN = "\\b(" + String.join("|", VALUE_TYPES) + ")\\b"; - private static final String PAREN_PATTERN = "\\(|\\)"; - private static final String BRACE_PATTERN = "\\{|\\}"; - private static final String BRACKET_PATTERN = "\\[|\\]"; - private static final String SEMICOLON_PATTERN = "\\;"; - private static final String STRING_PATTERN = "\"([^\"\\\\]|\\\\.)*\""; - private static final String COMMENT_PATTERN = "//[^\n]*" + "|" + "/\\*(.|\\R)*?\\*/"; - - private static final Pattern PATTERN = Pattern.compile( - "(?" + KEYWORD_PATTERN + ")" - + "|(?" + VALUE_TYPE_PATTERN + ")" - + "|(?" + PAREN_PATTERN + ")" - + "|(?" + BRACE_PATTERN + ")" - + "|(?" + BRACKET_PATTERN + ")" - + "|(?" + SEMICOLON_PATTERN + ")" - + "|(?" + STRING_PATTERN + ")" - + "|(?" + COMMENT_PATTERN + ")" - ); - - @FXThread - private static @NotNull StyleSpans> computeHighlighting(@NotNull final String text) { - - final Matcher matcher = PATTERN.matcher(text); - final StyleSpansBuilder> spansBuilder = new StyleSpansBuilder<>(); - - int lastKwEnd = 0; - - while (matcher.find()) { - - String styleClass = matcher.group("KEYWORD") != null ? "keyword" : null; - if (styleClass == null) { - styleClass = matcher.group("VALUETYPE") != null ? "value-type" : null; - } - - if (styleClass == null) { - styleClass = matcher.group("PAREN") != null ? "paren" : null; - } - - if (styleClass == null) { - styleClass = matcher.group("BRACE") != null ? "brace" : null; - } - - if (styleClass == null) { - styleClass = matcher.group("BRACKET") != null ? "bracket" : null; - } + /** + * The available fields. + */ + @NotNull + private final List fields; - if (styleClass == null) { - styleClass = matcher.group("SEMICOLON") != null ? "semicolon" : null; - } + /** + * THe pattern. + */ + @NotNull + private Pattern pattern; - if (styleClass == null) { - styleClass = matcher.group("STRING") != null ? "string" : null; - } + public GLSLCodeArea() { + this.pattern = buildPattern(); + this.fields = new ArrayList<>(); + } - if (styleClass == null) { - styleClass = matcher.group("COMMENT") != null ? "comment" : null; - } + /** + * Get available fields. + * + * @return available fields. + */ + @FXThread + private @Nullable List getFields() { + return fields; + } - assert styleClass != null; + /** + * Build the pattern to highlight code. + * + * @return the pattern to highlight code. + */ + @FXThread + private @NotNull Pattern buildPattern() { - spansBuilder.add(singleton("plain-code"), matcher.start() - lastKwEnd); - spansBuilder.add(singleton(styleClass), matcher.end() - matcher.start()); + String result = "(?<" + CLASS_KEYWORD + ">" + KEYWORD_PATTERN + ")" + + "|(?<" + CLASS_VALUE_TYPE + ">" + VALUE_TYPE_PATTERN + ")"; - lastKwEnd = matcher.end(); + final List fields = getFields(); + if (fields != null && !fields.isEmpty()) { + result += "|(?<" + CLASS_VALUE_VALUE + ">" + "\\b(" + String.join("|", fields) + ")\\b"; } - spansBuilder.add(singleton("plain-code"), text.length() - lastKwEnd); + result += "|(?<" + CLASS_PAREN + ">" + PAREN_PATTERN + ")" + + "|(?<" + CLASS_BRACE + ">" + BRACE_PATTERN + ")" + + "|(?<" + CLASS_BRACKET + ">" + BRACKET_PATTERN + ")" + + "|(?<" + CLASS_SEMICOLON + ">" + SEMICOLON_PATTERN + ")" + + "|(?<" + CLASS_STRING + ">" + STRING_PATTERN + ")" + + "|(?<" + CLASS_COMMENT + ">" + COMMENT_PATTERN + ")"; - return spansBuilder.create(); + return Pattern.compile(result); } @Override @FXThread protected @NotNull StyleSpans> calculateStyleSpans(@NotNull final String text) { - return computeHighlighting(text); + return computeHighlighting(pattern, text); } } diff --git a/src/main/java/com/ss/editor/ui/control/code/MaterialDefinitionCodeArea.java b/src/main/java/com/ss/editor/ui/control/code/MaterialDefinitionCodeArea.java index 27053b55..1c0bb521 100644 --- a/src/main/java/com/ss/editor/ui/control/code/MaterialDefinitionCodeArea.java +++ b/src/main/java/com/ss/editor/ui/control/code/MaterialDefinitionCodeArea.java @@ -1,13 +1,10 @@ package com.ss.editor.ui.control.code; -import static java.util.Collections.singleton; import com.ss.editor.annotation.FXThread; import org.fxmisc.richtext.model.StyleSpans; -import org.fxmisc.richtext.model.StyleSpansBuilder; import org.jetbrains.annotations.NotNull; import java.util.Collection; -import java.util.regex.Matcher; import java.util.regex.Pattern; /** @@ -57,12 +54,6 @@ public class MaterialDefinitionCodeArea extends BaseCodeArea { private static final String KEYWORD_PATTERN = "\\b(" + String.join("|", KEYWORDS) + ")\\b"; private static final String VALUE_TYPE_PATTERN = "\\b(" + String.join("|", VALUE_TYPES) + ")\\b"; private static final String VALUE_VALUE_PATTERN = "\\b(" + String.join("|", VALUE_VALUES) + ")\\b"; - private static final String PAREN_PATTERN = "\\(|\\)"; - private static final String BRACE_PATTERN = "\\{|\\}"; - private static final String BRACKET_PATTERN = "\\[|\\]"; - private static final String SEMICOLON_PATTERN = "\\;"; - private static final String STRING_PATTERN = "\"([^\"\\\\]|\\\\.)*\""; - private static final String COMMENT_PATTERN = "//[^\n]*" + "|" + "/\\*(.|\\R)*?\\*/"; private static final Pattern PATTERN = Pattern.compile( "(?" + KEYWORD_PATTERN + ")" @@ -76,66 +67,9 @@ public class MaterialDefinitionCodeArea extends BaseCodeArea { + "|(?" + COMMENT_PATTERN + ")" ); - @FXThread - private static @NotNull StyleSpans> computeHighlighting(final String text) { - - final Matcher matcher = PATTERN.matcher(text); - final StyleSpansBuilder> spansBuilder = new StyleSpansBuilder<>(); - - int lastKwEnd = 0; - - while (matcher.find()) { - - String styleClass = matcher.group("KEYWORD") != null ? "keyword" : null; - - if (styleClass == null) { - styleClass = matcher.group("VALUETYPE") != null ? "value-type" : null; - } - - if (styleClass == null) { - styleClass = matcher.group("VALUEVALUE") != null ? "value-value" : null; - } - - if (styleClass == null) { - styleClass = matcher.group("PAREN") != null ? "paren" : null; - } - - if (styleClass == null) { - styleClass = matcher.group("BRACE") != null ? "brace" : null; - } - - if (styleClass == null) { - styleClass = matcher.group("BRACKET") != null ? "bracket" : null; - } - - if (styleClass == null) { - styleClass = matcher.group("SEMICOLON") != null ? "semicolon" : null; - } - - if (styleClass == null) { - styleClass = matcher.group("STRING") != null ? "string" : null; - } - - if (styleClass == null) { - styleClass = matcher.group("COMMENT") != null ? "comment" : null; - } - - assert styleClass != null; - - spansBuilder.add(singleton("plain-code"), matcher.start() - lastKwEnd); - spansBuilder.add(singleton(styleClass), matcher.end() - matcher.start()); - - lastKwEnd = matcher.end(); - } - - spansBuilder.add(singleton("plain-code"), text.length() - lastKwEnd); - - return spansBuilder.create(); - } - @Override @FXThread protected @NotNull StyleSpans> calculateStyleSpans(@NotNull final String text) { - return computeHighlighting(text); + return computeHighlighting(PATTERN, text); } } diff --git a/src/main/java/com/ss/editor/util/GLSLType.java b/src/main/java/com/ss/editor/util/GLSLType.java new file mode 100644 index 00000000..cac5c6bb --- /dev/null +++ b/src/main/java/com/ss/editor/util/GLSLType.java @@ -0,0 +1,73 @@ +package com.ss.editor.util; + +import static com.ss.rlib.util.ObjectUtils.notNull; +import com.ss.editor.annotation.FromAnyThread; +import com.ss.rlib.util.dictionary.DictionaryFactory; +import com.ss.rlib.util.dictionary.ObjectDictionary; +import org.jetbrains.annotations.NotNull; + +/** + * The list of all GLSL types. + * + * @author JavaSaBr + */ +public enum GLSLType { + BOOL("bool"), + BOOL_VEC_2("bvec2"), + BOOL_VEC_3("bvec3"), + BOOL_VEC_4("bvec4"), + INT("int"), + INT_VEC_2("ivec2"), + INT_VEC_3("ivec3"), + INT_VEC_4("ivec4"), + UNSIGNED_INT("uint"), + UNSIGNED_INT_VEC_2("uvec2"), + UNSIGNED_INT_VEC_3("uvec3"), + UNSIGNED_INT_VEC_4("uvec4"), + FLOAT("float"), + VEC_2("vec2"), + VEC_3("vec3"), + VEC_4("vec4"), + MAT_2("mat2"), + MAT_3("mat3"), + MAT_4("mat4"), + SAMPLER_2D("sampler2D"), + SAMPLER_CUBE("samplerCube"); + + @NotNull + public static final GLSLType[] VALUES = values(); + + @NotNull + private static final ObjectDictionary RAW_TYPE_TO_ENUM = DictionaryFactory.newObjectDictionary(); + + /** + * Get the enum value for the raw type. + * + * @param rawType the raw type. + * @return the enum value. + */ + @FromAnyThread + public static @NotNull GLSLType of(@NotNull final String rawType) { + return notNull(RAW_TYPE_TO_ENUM.get(rawType)); + } + + /** + * The type to use in shaders. + */ + @NotNull + private String rawType; + + GLSLType(@NotNull final String rawType) { + this.rawType = rawType; + } + + /** + * Get the type to use in shaders. + * + * @return the type to use in shaders. + */ + @FromAnyThread + public @NotNull String getRawType() { + return rawType; + } +} From 1ded13e8ee0b55d2f72fa7d6acefe0ed25fe8079 Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Fri, 22 Sep 2017 10:40:19 +0300 Subject: [PATCH 39/50] fixed resource control. --- .../api/property/control/ResourcePropertyEditorControl.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/ss/editor/plugin/api/property/control/ResourcePropertyEditorControl.java b/src/main/java/com/ss/editor/plugin/api/property/control/ResourcePropertyEditorControl.java index 604124da..fca48260 100644 --- a/src/main/java/com/ss/editor/plugin/api/property/control/ResourcePropertyEditorControl.java +++ b/src/main/java/com/ss/editor/plugin/api/property/control/ResourcePropertyEditorControl.java @@ -64,9 +64,10 @@ protected void createComponents() { changeButton.setGraphic(new ImageView(Icons.ADD_16)); changeButton.setOnAction(event -> processSelect()); - resourceLabel.prefWidthProperty().bind(widthProperty().multiply(DEFAULT_FIELD_W_PERCENT)); - final HBox container = new HBox(resourceLabel, changeButton); + container.prefWidthProperty().bind(widthProperty().multiply(DEFAULT_FIELD_W_PERCENT)); + + resourceLabel.prefWidthProperty().bind(container.widthProperty()); FXUtils.addToPane(container, this); From ba877a76673b0d935947bae841ebb4b247725dac Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Fri, 22 Sep 2017 12:44:20 +0300 Subject: [PATCH 40/50] moved rename action. --- .../model/node/spatial/SpatialTreeNode.java | 14 +++----------- .../ss/editor/ui/control/tree/node/TreeNode.java | 2 ++ 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/ss/editor/ui/control/model/node/spatial/SpatialTreeNode.java b/src/main/java/com/ss/editor/ui/control/model/node/spatial/SpatialTreeNode.java index 679900e9..4d1e8bd8 100644 --- a/src/main/java/com/ss/editor/ui/control/model/node/spatial/SpatialTreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/model/node/spatial/SpatialTreeNode.java @@ -26,7 +26,6 @@ import com.ss.editor.ui.control.model.tree.ModelNodeTree; import com.ss.editor.ui.control.model.tree.action.AddUserDataAction; import com.ss.editor.ui.control.model.tree.action.RemoveNodeAction; -import com.ss.editor.ui.control.model.tree.action.RenameNodeAction; import com.ss.editor.ui.control.model.tree.action.control.CreateCustomControlAction; import com.ss.editor.ui.control.model.tree.action.control.CreateMotionControlAction; import com.ss.editor.ui.control.model.tree.action.control.physics.CreateCharacterControlAction; @@ -75,7 +74,6 @@ public void fillContextMenu(@NotNull final NodeTree nodeTree, if (toolMenu != null) items.add(toolMenu); } - if (canEditName()) items.add(new RenameNodeAction(nodeTree, this)); if (canRemove()) items.add(new RemoveNodeAction(nodeTree, this)); if (linkNode == null) { @@ -100,14 +98,8 @@ public boolean canCopy() { @Override @FXThread public boolean canAccept(@NotNull final TreeNode child, final boolean isCopy) { - final Object element = child.getElement(); - - if (element instanceof AbstractControl) { - return true; - } - - return super.canAccept(child, isCopy); + return element instanceof AbstractControl || super.canAccept(child, isCopy); } @Override @@ -227,9 +219,9 @@ public void changeName(@NotNull final NodeTree nodeTree, @NotNull final Strin consumer.execute(new RenameNodeOperation(spatial.getName(), newName, spatial)); } - @NotNull @Override - public Array> getChildren(@NotNull final NodeTree nodeTree) { + @FXThread + public @NotNull Array> getChildren(@NotNull final NodeTree nodeTree) { final Array> result = ArrayFactory.newArray(TreeNode.class); final Spatial element = getElement(); diff --git a/src/main/java/com/ss/editor/ui/control/tree/node/TreeNode.java b/src/main/java/com/ss/editor/ui/control/tree/node/TreeNode.java index f67880a0..6a11bb33 100644 --- a/src/main/java/com/ss/editor/ui/control/tree/node/TreeNode.java +++ b/src/main/java/com/ss/editor/ui/control/tree/node/TreeNode.java @@ -5,6 +5,7 @@ import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.model.UObject; import com.ss.editor.model.undo.editor.ChangeConsumer; +import com.ss.editor.ui.control.model.tree.action.RenameNodeAction; import com.ss.editor.ui.control.tree.NodeTree; import com.ss.rlib.util.array.Array; import com.ss.rlib.util.array.ArrayFactory; @@ -170,6 +171,7 @@ protected void setParent(@Nullable final TreeNode parent) { */ @FXThread public void fillContextMenu(@NotNull final NodeTree nodeTree, @NotNull final ObservableList items) { + if (canEditName()) items.add(new RenameNodeAction(nodeTree, this)); } /** From e7a9927754df833a18a41a5ecb1663c3f7a992de Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Sat, 23 Sep 2017 11:25:23 +0300 Subject: [PATCH 41/50] refactoring property builders. --- .../builder/impl/AppStatePropertyBuilder.java | 14 +-- .../builder/impl/FilterPropertyBuilder.java | 14 +-- .../MaterialSettingsPropertyBuilder.java | 27 +++++ .../impl/AudioNodePropertyBuilder.java | 12 +- .../impl/CollisionShapePropertyBuilder.java | 14 ++- .../impl/DefaultControlPropertyBuilder.java | 18 ++- .../impl/EditableControlPropertyBuilder.java | 8 +- .../EditableModelObjectPropertyBuilder.java | 2 + .../impl/EmitterShapePropertyBuilder.java | 29 +++-- .../builder/impl/GeometryPropertyBuilder.java | 19 ++-- .../builder/impl/LightPropertyBuilder.java | 12 +- .../builder/impl/MaterialPropertyBuilder.java | 61 ++++++++-- .../builder/impl/MeshPropertyBuilder.java | 8 +- .../impl/ParticleEmitterPropertyBuilder.java | 14 ++- .../ParticleInfluencerPropertyBuilder.java | 15 ++- .../impl/PrimitivePropertyBuilder.java | 6 +- .../builder/impl/SpatialPropertyBuilder.java | 10 +- ...eg0dParticleInfluencerPropertyBuilder.java | 106 ++++++++++-------- .../builder/impl/AbstractPropertyBuilder.java | 28 ++--- .../impl/EditableObjectPropertyBuilder.java | 10 ++ 20 files changed, 300 insertions(+), 127 deletions(-) diff --git a/src/main/java/com/ss/editor/ui/control/app/state/property/builder/impl/AppStatePropertyBuilder.java b/src/main/java/com/ss/editor/ui/control/app/state/property/builder/impl/AppStatePropertyBuilder.java index 4e310ec5..e0eb55e6 100644 --- a/src/main/java/com/ss/editor/ui/control/app/state/property/builder/impl/AppStatePropertyBuilder.java +++ b/src/main/java/com/ss/editor/ui/control/app/state/property/builder/impl/AppStatePropertyBuilder.java @@ -1,5 +1,7 @@ package com.ss.editor.ui.control.app.state.property.builder.impl; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.extension.property.EditableProperty; import com.ss.editor.extension.scene.app.state.EditableSceneAppState; import com.ss.editor.model.undo.editor.SceneChangeConsumer; @@ -20,23 +22,21 @@ public class AppStatePropertyBuilder extends EditableModelObjectPropertyBuilder private static final AppStatePropertyBuilder INSTANCE = new AppStatePropertyBuilder(); /** - * Gets instance. + * Get the single instance. * - * @return the instance + * @return the single instance. */ - @NotNull - public static AppStatePropertyBuilder getInstance() { + @FromAnyThread + public static @NotNull AppStatePropertyBuilder getInstance() { return INSTANCE; } - /** - * Instantiates a new App state property builder. - */ protected AppStatePropertyBuilder() { super(SceneChangeConsumer.class); } @Override + @FXThread protected @Nullable List> getProperties(@NotNull final Object object) { if (!(object instanceof EditableSceneAppState)) return null; final EditableSceneAppState state = (EditableSceneAppState) object; diff --git a/src/main/java/com/ss/editor/ui/control/filter/property/builder/impl/FilterPropertyBuilder.java b/src/main/java/com/ss/editor/ui/control/filter/property/builder/impl/FilterPropertyBuilder.java index 008606e1..a6d1bf0e 100644 --- a/src/main/java/com/ss/editor/ui/control/filter/property/builder/impl/FilterPropertyBuilder.java +++ b/src/main/java/com/ss/editor/ui/control/filter/property/builder/impl/FilterPropertyBuilder.java @@ -1,5 +1,7 @@ package com.ss.editor.ui.control.filter.property.builder.impl; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.extension.property.EditableProperty; import com.ss.editor.extension.scene.filter.EditableSceneFilter; import com.ss.editor.model.undo.editor.ModelChangeConsumer; @@ -20,23 +22,21 @@ public class FilterPropertyBuilder extends EditableModelObjectPropertyBuilder { private static final FilterPropertyBuilder INSTANCE = new FilterPropertyBuilder(); /** - * Gets instance. + * Get the single instance. * - * @return the instance + * @return the single instance */ - @NotNull - public static FilterPropertyBuilder getInstance() { + @FromAnyThread + public static @NotNull FilterPropertyBuilder getInstance() { return INSTANCE; } - /** - * Instantiates a new Filter property builder. - */ protected FilterPropertyBuilder() { super(ModelChangeConsumer.class); } @Override + @FXThread protected @Nullable List> getProperties(@NotNull final Object object) { if (!(object instanceof EditableSceneFilter)) return null; final EditableSceneFilter filter = (EditableSceneFilter) object; diff --git a/src/main/java/com/ss/editor/ui/control/material/property/builder/MaterialSettingsPropertyBuilder.java b/src/main/java/com/ss/editor/ui/control/material/property/builder/MaterialSettingsPropertyBuilder.java index dcfbb16c..45bf88c7 100644 --- a/src/main/java/com/ss/editor/ui/control/material/property/builder/MaterialSettingsPropertyBuilder.java +++ b/src/main/java/com/ss/editor/ui/control/material/property/builder/MaterialSettingsPropertyBuilder.java @@ -7,6 +7,8 @@ import com.jme3.material.RenderState; import com.jme3.shader.VarType; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.extension.property.EditableProperty; import com.ss.editor.extension.property.EditablePropertyType; import com.ss.editor.extension.property.SimpleProperty; @@ -54,13 +56,21 @@ public class MaterialSettingsPropertyBuilder extends MaterialPropertyBuilder { COLOR_TYPES.add(VarType.Vector4); } + @NotNull private static final PropertyBuilder INSTANCE = new MaterialSettingsPropertyBuilder(); + /** + * Get the single instance. + * + * @return the single instance. + */ + @FromAnyThread public static @NotNull PropertyBuilder getInstance() { return INSTANCE; } @Override + @FXThread protected @Nullable List> getProperties(@NotNull final Object object) { if (!(object instanceof MaterialSettings) || object instanceof RootMaterialSettings) { @@ -120,6 +130,14 @@ public class MaterialSettingsPropertyBuilder extends MaterialPropertyBuilder { .collect(Collectors.toList()); } + /** + * Filter material parameters for the presented object. + * + * @param param the material parameter. + * @param object the presented object. + * @return true of we can show the parameter. + */ + @FXThread private boolean filter(@NotNull final MatParam param, @NotNull final Object object) { if (object instanceof TexturesSettings) { @@ -131,6 +149,15 @@ private boolean filter(@NotNull final MatParam param, @NotNull final Object obje return !TEXTURE_TYPES.contains(param.getVarType()) && !COLOR_TYPES.contains(param.getVarType()); } + /** + * Convert the material parameter to an editable property. + * + * @param param the material parameter. + * @param material the material. + * @param settings the settings. + * @return the editable property or null. + */ + @FXThread private @Nullable EditableProperty convert(@NotNull final MatParam param, @NotNull final Material material, @NotNull final MaterialSettings settings) { diff --git a/src/main/java/com/ss/editor/ui/control/model/property/builder/impl/AudioNodePropertyBuilder.java b/src/main/java/com/ss/editor/ui/control/model/property/builder/impl/AudioNodePropertyBuilder.java index e4d4163f..68561037 100644 --- a/src/main/java/com/ss/editor/ui/control/model/property/builder/impl/AudioNodePropertyBuilder.java +++ b/src/main/java/com/ss/editor/ui/control/model/property/builder/impl/AudioNodePropertyBuilder.java @@ -6,6 +6,8 @@ import com.jme3.audio.AudioNode; import com.jme3.math.Vector3f; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.model.undo.editor.ModelChangeConsumer; import com.ss.editor.ui.control.property.builder.PropertyBuilder; import com.ss.editor.ui.control.property.builder.impl.AbstractPropertyBuilder; @@ -45,10 +47,11 @@ public class AudioNodePropertyBuilder extends AbstractPropertyBuilder> getProperties(@NotNull final Object object) { if(!(object instanceof EditableControl)) return null; return ((EditableControl) object).getEditableProperties(); diff --git a/src/main/java/com/ss/editor/ui/control/model/property/builder/impl/EditableModelObjectPropertyBuilder.java b/src/main/java/com/ss/editor/ui/control/model/property/builder/impl/EditableModelObjectPropertyBuilder.java index 7a9bf66f..f48f82ac 100644 --- a/src/main/java/com/ss/editor/ui/control/model/property/builder/impl/EditableModelObjectPropertyBuilder.java +++ b/src/main/java/com/ss/editor/ui/control/model/property/builder/impl/EditableModelObjectPropertyBuilder.java @@ -4,6 +4,7 @@ import com.jme3.light.PointLight; import com.jme3.scene.Node; import com.jme3.scene.Spatial; +import com.ss.editor.annotation.FXThread; import com.ss.editor.extension.property.EditableProperty; import com.ss.editor.extension.property.EditablePropertyType; import com.ss.editor.model.undo.editor.ModelChangeConsumer; @@ -27,6 +28,7 @@ protected EditableModelObjectPropertyBuilder(@NotNull final Class description) { super.buildFor(container, changeConsumer, description); diff --git a/src/main/java/com/ss/editor/ui/control/model/property/builder/impl/EmitterShapePropertyBuilder.java b/src/main/java/com/ss/editor/ui/control/model/property/builder/impl/EmitterShapePropertyBuilder.java index 58ef2822..7a689d2b 100644 --- a/src/main/java/com/ss/editor/ui/control/model/property/builder/impl/EmitterShapePropertyBuilder.java +++ b/src/main/java/com/ss/editor/ui/control/model/property/builder/impl/EmitterShapePropertyBuilder.java @@ -6,6 +6,8 @@ import com.jme3.effect.shapes.EmitterSphereShape; import com.jme3.math.Vector3f; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.model.undo.editor.ModelChangeConsumer; import com.ss.editor.ui.control.property.builder.PropertyBuilder; import com.ss.editor.ui.control.property.builder.impl.AbstractPropertyBuilder; @@ -27,10 +29,11 @@ public class EmitterShapePropertyBuilder extends AbstractPropertyBuilder> getProperties(final @NotNull Object object) { if(!(object instanceof Material)) return null; @@ -82,6 +86,14 @@ protected MaterialPropertyBuilder() { .collect(toList()); } + /** + * Convert the material parameter to editable property. + * + * @param param the material parameter. + * @param material the material. + * @return the editable property or null. + */ + @FXThread private @Nullable EditableProperty convert(@NotNull final MatParam param, @NotNull final Material material) { @@ -95,28 +107,57 @@ protected MaterialPropertyBuilder() { (object, newValue) -> applyParam(param, object, newValue)); } - protected void applyParam(@NotNull final MatParam param, @NotNull final Material object, + /** + * Apply changes for the material parameter. + * + * @param param the parameter. + * @param material the material. + * @param newValue the new value. + */ + @FXThread + protected void applyParam(@NotNull final MatParam param, @NotNull final Material material, @Nullable final Object newValue) { + if (newValue == null) { - object.clearParam(param.getName()); + material.clearParam(param.getName()); } else { - object.setParam(param.getName(), param.getVarType(), newValue); + material.setParam(param.getName(), param.getVarType(), newValue); } } + /** + * Get relevant value of the material parameter. + * + * @param param the material parameter. + * @param material the material. + * @return the relevant value. + */ + @FXThread protected @Nullable Object getParamValue(@NotNull final MatParam param, @NotNull final Material material) { final MatParam currentParam = material.getParam(param.getName()); return currentParam == null ? null : currentParam.getValue(); } + /** + * Convert material parameter type to editable property type. + * + * @param varType the material parameter type. + * @return the editable property type or null. + */ + @FXThread protected @Nullable EditablePropertyType convert(@NotNull final VarType varType) { switch (varType) { - case Boolean: return EditablePropertyType.BOOLEAN; - case Float: return EditablePropertyType.FLOAT; - case Int: return EditablePropertyType.INTEGER; - case Vector4: return EditablePropertyType.COLOR; - case Texture2D: return EditablePropertyType.TEXTURE_2D; + case Boolean: + return EditablePropertyType.BOOLEAN; + case Float: + return EditablePropertyType.FLOAT; + case Int: + return EditablePropertyType.INTEGER; + case Vector4: + return EditablePropertyType.COLOR; + case Texture2D: + return EditablePropertyType.TEXTURE_2D; } return null; diff --git a/src/main/java/com/ss/editor/ui/control/model/property/builder/impl/MeshPropertyBuilder.java b/src/main/java/com/ss/editor/ui/control/model/property/builder/impl/MeshPropertyBuilder.java index cd7af4de..565713d1 100644 --- a/src/main/java/com/ss/editor/ui/control/model/property/builder/impl/MeshPropertyBuilder.java +++ b/src/main/java/com/ss/editor/ui/control/model/property/builder/impl/MeshPropertyBuilder.java @@ -2,6 +2,8 @@ import com.jme3.scene.Mesh; import com.ss.editor.Messages; +import com.ss.editor.annotation.FXThread; +import com.ss.editor.annotation.FromAnyThread; import com.ss.editor.model.undo.editor.ModelChangeConsumer; import com.ss.editor.ui.control.property.builder.PropertyBuilder; import com.ss.editor.ui.control.property.builder.impl.AbstractPropertyBuilder; @@ -26,10 +28,11 @@ public class MeshPropertyBuilder extends AbstractPropertyBuilder implements PropertyBuilder { /** - * The constant EDITOR. + * The jME part of the editor. */ @NotNull protected static final Editor EDITOR = Editor.getInstance(); + /** + * The type of change consumer, + */ @NotNull private final Class type; - /** - * Instantiates a new Abstract property builder. - * - * @param type the type - */ protected AbstractPropertyBuilder(@NotNull final Class type) { this.type = type; } @Override + @FXThread public void buildFor(@NotNull final Object object, @Nullable final Object parent, @NotNull final VBox container, @NotNull final ChangeConsumer changeConsumer) { @@ -47,15 +47,16 @@ public void buildFor(@NotNull final Object object, @Nullable final Object parent } /** - * Build for. + * Build properties for the object. * - * @param object the object - * @param parent the parent - * @param container the container - * @param changeConsumer the change consumer + * @param object the object. + * @param parent the parent. + * @param container the container. + * @param changeConsumer the change consumer. */ - protected void buildForImpl(@NotNull final Object object, @Nullable final Object parent, @NotNull final VBox container, - @NotNull final C changeConsumer) { + @FXThread + protected void buildForImpl(@NotNull final Object object, @Nullable final Object parent, + @NotNull final VBox container, @NotNull final C changeConsumer) { } /** @@ -63,6 +64,7 @@ protected void buildForImpl(@NotNull final Object object, @Nullable final Object * * @param pane the container of the line. */ + @FXThread protected void buildSplitLine(@NotNull final Pane pane) { final HBox line = new HBox(); final VBox container = new VBox(line); diff --git a/src/main/java/com/ss/editor/ui/control/property/builder/impl/EditableObjectPropertyBuilder.java b/src/main/java/com/ss/editor/ui/control/property/builder/impl/EditableObjectPropertyBuilder.java index 52fbc334..df6630a3 100644 --- a/src/main/java/com/ss/editor/ui/control/property/builder/impl/EditableObjectPropertyBuilder.java +++ b/src/main/java/com/ss/editor/ui/control/property/builder/impl/EditableObjectPropertyBuilder.java @@ -6,6 +6,7 @@ import com.jme3.math.Vector3f; import com.jme3.math.Vector4f; import com.jme3.texture.Texture2D; +import com.ss.editor.annotation.FXThread; import com.ss.editor.extension.property.EditableProperty; import com.ss.editor.extension.property.EditablePropertyType; import com.ss.editor.model.undo.editor.ChangeConsumer; @@ -33,6 +34,7 @@ protected EditableObjectPropertyBuilder(@NotNull final Class type) } @Override + @FXThread protected void buildForImpl(@NotNull final Object object, @Nullable final Object parent, @NotNull final VBox container, @NotNull final C changeConsumer) { @@ -44,6 +46,7 @@ protected void buildForImpl(@NotNull final Object object, @Nullable final Object } } + @FXThread protected void buildFor(@NotNull final VBox container, @NotNull final C changeConsumer, @NotNull final EditableProperty description) { @@ -165,6 +168,10 @@ protected void buildFor(@NotNull final VBox container, @NotNull final C changeCo addControl(container, property, propertyControl); break; } + case SEPARATOR: { + buildSplitLine(container); + break; + } default: { break; } @@ -179,6 +186,7 @@ protected void buildFor(@NotNull final VBox container, @NotNull final C changeCo * @param property the property * @param propertyControl the property control */ + @FXThread protected void addControl(@NotNull final VBox container, @NotNull final EditableProperty property, @NotNull final PropertyControl, T> propertyControl) { @@ -195,10 +203,12 @@ protected void addControl(@NotNull final VBox container, @NotNull final Edit * @param object the editable object. * @return the list of properties or null. */ + @FXThread protected @Nullable List> getProperties(final @NotNull Object object) { return null; } + @FXThread protected @NotNull EditableProperty cast(@NotNull final EditableProperty property) { return ClassUtils.unsafeCast(property); } From 0d56c15731ceaffd890b3652a0f70a6c9b9557fc Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Sat, 23 Sep 2017 12:17:34 +0300 Subject: [PATCH 42/50] updated the dependencies --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 7b672157..ff7bbae6 100644 --- a/build.gradle +++ b/build.gradle @@ -125,7 +125,7 @@ dependencies { compile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.3' // extensions - compile ('com.github.JavaSaBr:jme3-spaceshift-extension:1.7.0') { + compile ('com.github.JavaSaBr:jme3-spaceshift-extension:1.7.1') { exclude group: 'org.jmonkeyengine' } compile ('com.github.JavaSaBr:tonegodemitter:2.4.0') { From f031324e6d2837b00f5d15d6e5f5161a3931e94c Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Sat, 23 Sep 2017 13:07:53 +0300 Subject: [PATCH 43/50] added control for string read only properties --- .../builder/impl/EditableObjectPropertyBuilder.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/com/ss/editor/ui/control/property/builder/impl/EditableObjectPropertyBuilder.java b/src/main/java/com/ss/editor/ui/control/property/builder/impl/EditableObjectPropertyBuilder.java index df6630a3..eac70054 100644 --- a/src/main/java/com/ss/editor/ui/control/property/builder/impl/EditableObjectPropertyBuilder.java +++ b/src/main/java/com/ss/editor/ui/control/property/builder/impl/EditableObjectPropertyBuilder.java @@ -123,6 +123,17 @@ protected void buildFor(@NotNull final VBox container, @NotNull final C changeCo addControl(container, property, propertyControl); break; } + case READ_ONLY_STRING: { + + final EditableProperty property = cast(description); + final Object currentValue = property.getValue(); + + final DefaultSinglePropertyControl, Object> propertyControl = + new DefaultSinglePropertyControl<>(currentValue, property.getName(), changeConsumer); + + addControl(container, property, propertyControl); + break; + } case VECTOR_2F: { final EditableProperty property = cast(description); From 4c01a25ade682f911d285483150df8d1dc372126 Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Sat, 23 Sep 2017 13:41:45 +0300 Subject: [PATCH 44/50] updated GLSLType enum. --- .../java/com/ss/editor/util/GLSLType.java | 90 +++++++++++++------ 1 file changed, 64 insertions(+), 26 deletions(-) diff --git a/src/main/java/com/ss/editor/util/GLSLType.java b/src/main/java/com/ss/editor/util/GLSLType.java index cac5c6bb..7700e936 100644 --- a/src/main/java/com/ss/editor/util/GLSLType.java +++ b/src/main/java/com/ss/editor/util/GLSLType.java @@ -12,27 +12,27 @@ * @author JavaSaBr */ public enum GLSLType { - BOOL("bool"), - BOOL_VEC_2("bvec2"), - BOOL_VEC_3("bvec3"), - BOOL_VEC_4("bvec4"), - INT("int"), - INT_VEC_2("ivec2"), - INT_VEC_3("ivec3"), - INT_VEC_4("ivec4"), - UNSIGNED_INT("uint"), - UNSIGNED_INT_VEC_2("uvec2"), - UNSIGNED_INT_VEC_3("uvec3"), - UNSIGNED_INT_VEC_4("uvec4"), - FLOAT("float"), - VEC_2("vec2"), - VEC_3("vec3"), - VEC_4("vec4"), - MAT_2("mat2"), - MAT_3("mat3"), - MAT_4("mat4"), - SAMPLER_2D("sampler2D"), - SAMPLER_CUBE("samplerCube"); + BOOL("bool", "Boolean"), + BOOL_VEC_2("bvec2", "Boolean vector x 2"), + BOOL_VEC_3("bvec3", "Boolean vector x 3"), + BOOL_VEC_4("bvec4", "Boolean vector x 4"), + INT("int", "Integer"), + INT_VEC_2("ivec2", "Integer vector x 2"), + INT_VEC_3("ivec3", "Integer vector x 3"), + INT_VEC_4("ivec4", "Integer vector x 4"), + UNSIGNED_INT("uint", "Unsigned integer"), + UNSIGNED_INT_VEC_2("uvec2", "Unsigned integer vector x 2"), + UNSIGNED_INT_VEC_3("uvec3", "Unsigned integer vector x 3"), + UNSIGNED_INT_VEC_4("uvec4", "Unsigned integer vector x 4"), + FLOAT("float", "Float"), + VEC_2("vec2", "Float vector x 2"), + VEC_3("vec3", "Float vector x 3"), + VEC_4("vec4", "Float vector x 4"), + MAT_2("mat2", "Matrix x 2"), + MAT_3("mat3", "Matrix x 3"), + MAT_4("mat4", "Matrix x 4"), + SAMPLER_2D("sampler2D", "Texture 2D"), + SAMPLER_CUBE("samplerCube", "Cube Texture"); @NotNull public static final GLSLType[] VALUES = values(); @@ -40,6 +40,16 @@ public enum GLSLType { @NotNull private static final ObjectDictionary RAW_TYPE_TO_ENUM = DictionaryFactory.newObjectDictionary(); + @NotNull + private static final ObjectDictionary UI_NAME_TO_ENUM = DictionaryFactory.newObjectDictionary(); + + static { + for (final GLSLType glslType : VALUES) { + RAW_TYPE_TO_ENUM.put(glslType.getRawType(), glslType); + UI_NAME_TO_ENUM.put(glslType.getUiName(), glslType); + } + } + /** * Get the enum value for the raw type. * @@ -47,27 +57,55 @@ public enum GLSLType { * @return the enum value. */ @FromAnyThread - public static @NotNull GLSLType of(@NotNull final String rawType) { + public static @NotNull GLSLType ofRawType(@NotNull final String rawType) { return notNull(RAW_TYPE_TO_ENUM.get(rawType)); } /** - * The type to use in shaders. + * Get the enum value for the UI name. + * + * @param uiName the UI name. + * @return the enum value. + */ + @FromAnyThread + public static @NotNull GLSLType ofUIName(@NotNull final String uiName) { + return notNull(RAW_TYPE_TO_ENUM.get(uiName)); + } + + /** + * The type to use in a shader. */ @NotNull private String rawType; - GLSLType(@NotNull final String rawType) { + /** + * The name for UI. + */ + @NotNull + private String uiName; + + GLSLType(@NotNull final String rawType, @NotNull final String uiName) { this.rawType = rawType; + this.uiName = uiName; } /** - * Get the type to use in shaders. + * Get the type to use in a shader. * - * @return the type to use in shaders. + * @return the type to use in a shader. */ @FromAnyThread public @NotNull String getRawType() { return rawType; } + + /** + * Get the name for UI. + * + * @return the name for UI. + */ + @FromAnyThread + public @NotNull String getUiName() { + return uiName; + } } From b47013d0213a8923213669cd342b4438f42e0d70 Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Wed, 27 Sep 2017 08:01:22 +0300 Subject: [PATCH 45/50] fixed a problem with clearing cache of not cached asset keys. --- src/main/java/com/ss/editor/manager/ResourceManager.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/com/ss/editor/manager/ResourceManager.java b/src/main/java/com/ss/editor/manager/ResourceManager.java index 2d306d32..e6a6f0a2 100644 --- a/src/main/java/com/ss/editor/manager/ResourceManager.java +++ b/src/main/java/com/ss/editor/manager/ResourceManager.java @@ -252,6 +252,9 @@ public synchronized void registerInterestedFileType(@NotNull final String fileEx @Override @FromAnyThread public synchronized void assetLoaded(@NotNull final AssetKey key) { + if (key.getCacheType() == null) { + return; + } final String extension = key.getExtension(); if (StringUtils.isEmpty(extension)) return; @@ -264,6 +267,9 @@ public synchronized void assetLoaded(@NotNull final AssetKey key) { @Override @FromAnyThread public synchronized void assetRequested(@NotNull final AssetKey key) { + if (key.getCacheType() == null) { + return; + } final String extension = key.getExtension(); if (StringUtils.isEmpty(extension)) return; From 804a8529d622ebe2827c94e78fb4476082014964 Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Wed, 27 Sep 2017 08:02:56 +0300 Subject: [PATCH 46/50] fixed string presentation of GLSLType. --- src/main/java/com/ss/editor/util/GLSLType.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/ss/editor/util/GLSLType.java b/src/main/java/com/ss/editor/util/GLSLType.java index 7700e936..dec0f06d 100644 --- a/src/main/java/com/ss/editor/util/GLSLType.java +++ b/src/main/java/com/ss/editor/util/GLSLType.java @@ -46,7 +46,7 @@ public enum GLSLType { static { for (final GLSLType glslType : VALUES) { RAW_TYPE_TO_ENUM.put(glslType.getRawType(), glslType); - UI_NAME_TO_ENUM.put(glslType.getUiName(), glslType); + UI_NAME_TO_ENUM.put(glslType.getUIName(), glslType); } } @@ -105,7 +105,12 @@ public enum GLSLType { * @return the name for UI. */ @FromAnyThread - public @NotNull String getUiName() { + public @NotNull String getUIName() { return uiName; } + + @Override + public String toString() { + return getUIName(); + } } From 2dcd20493da18c274ab19d77331803f05c65f705 Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Fri, 29 Sep 2017 06:44:14 +0300 Subject: [PATCH 47/50] fixed a problem with code area component. --- .../java/com/ss/editor/ui/control/code/BaseCodeArea.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/ss/editor/ui/control/code/BaseCodeArea.java b/src/main/java/com/ss/editor/ui/control/code/BaseCodeArea.java index 7b5d6a2a..77c8135c 100644 --- a/src/main/java/com/ss/editor/ui/control/code/BaseCodeArea.java +++ b/src/main/java/com/ss/editor/ui/control/code/BaseCodeArea.java @@ -165,7 +165,12 @@ public void reloadContent(@NotNull final String content, final boolean clearHist if (!StringUtils.equals(currentContent, content)) { if (content.isEmpty()) { - deleteText(0, currentContent.length()); + try { + clear(); + } catch (final IllegalStateException e) { + //FIXME it's a bug in the richfxeditor library + e.printStackTrace(); + } } else { replaceText(0, currentContent.length(), content); } From c658243c1841c571cd78cc1730bcdd636d64e03f Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Sat, 30 Sep 2017 14:34:07 +0300 Subject: [PATCH 48/50] added shander nodes plugin, added temp fixes. --- README.md | 2 +- .../ss-editor-shader-nodes-1.0.0.jar | Bin 0 -> 266818 bytes src/main/java/com/jme3/material/MatParam.java | 413 ++++++++++++++++++ .../java/com/jme3/scene/AssetLinkNode.java | 197 +++++++++ .../com/jme3/shader/plugins/GLSLLoader.java | 197 +++++++++ .../StringFromListPropertyEditorControl.java | 18 +- 6 files changed, 816 insertions(+), 11 deletions(-) create mode 100644 embedded-plugins/ss-editor-shader-nodes/ss-editor-shader-nodes-1.0.0.jar create mode 100644 src/main/java/com/jme3/material/MatParam.java create mode 100644 src/main/java/com/jme3/scene/AssetLinkNode.java create mode 100644 src/main/java/com/jme3/shader/plugins/GLSLLoader.java diff --git a/README.md b/README.md index c6472fbc..cc189d92 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# jMonkeyEngine 3 SpaceShift Editor 1.1.0 # +# jMonkeyEngine 3 SpaceShift Editor 1.2.0 # ## License: Apache Version 2.0 ## [![Join the chat at https://gitter.im/jME3-SpaceShift-Editor/Lobby](https://badges.gitter.im/jME3-SpaceShift-Editor/Lobby.svg)](https://gitter.im/jME3-SpaceShift-Editor/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) diff --git a/embedded-plugins/ss-editor-shader-nodes/ss-editor-shader-nodes-1.0.0.jar b/embedded-plugins/ss-editor-shader-nodes/ss-editor-shader-nodes-1.0.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..bd8eb508ae97f4c06ea2c975a77debee2be4c387 GIT binary patch literal 266818 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 XWH2EF2iFMxp#?jSP>R613f%tyFkDyk literal 0 HcmV?d00001 diff --git a/src/main/java/com/jme3/material/MatParam.java b/src/main/java/com/jme3/material/MatParam.java new file mode 100644 index 00000000..15706ff3 --- /dev/null +++ b/src/main/java/com/jme3/material/MatParam.java @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2009-2012 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.material; + +import com.jme3.asset.TextureKey; +import com.jme3.export.*; +import com.jme3.math.*; +import com.jme3.shader.VarType; +import com.jme3.texture.Texture; +import com.jme3.texture.Texture.WrapMode; + +import java.io.IOException; + +/** + * Describes a material parameter. This is used for both defining a name and type + * as well as a material parameter value. + * + * @author Kirill Vainer + */ +public class MatParam implements Savable, Cloneable { + + protected VarType type; + protected String name; + protected String prefixedName; + protected Object value; + + /** + * Create a new material parameter. For internal use only. + */ + public MatParam(VarType type, String name, Object value) { + this.type = type; + this.name = name; + this.prefixedName = "m_" + name; + this.value = value; + } + + /** + * Serialization only. Do not use. + */ + public MatParam() { + } + + /** + * Returns the material parameter type. + * + * @return the material parameter type. + */ + public VarType getVarType() { + return type; + } + + /** + * Returns the name of the material parameter. + * @return the name of the material parameter. + */ + public String getName() { + return name; + } + + /** + * Returns the name with "m_" prefixed to it. + * + * @return the name with "m_" prefixed to it + */ + public String getPrefixedName() { + return prefixedName; + } + + /** + * Used internally + * @param name + */ + void setName(String name) { + this.name = name; + this.prefixedName = "m_" + name; + } + + /** + * Returns the value of this material parameter. + *

+ * Material parameters that are used for material definitions + * will not have a value, unless there's a default value declared + * in the definition. + * + * @return the value of this material parameter. + */ + public Object getValue() { + return value; + } + + /** + * Sets the value of this material parameter. + *

+ * It is assumed the value is of the same {@link MatParam#getVarType() type} + * as this material parameter. + * + * @param value the value of this material parameter. + */ + public void setValue(Object value) { + this.value = value; + } + + + /** + * Returns the material parameter value as it would appear in a J3M + * file. E.g.
+ * + * MaterialParameters {
+ * ABC : 1 2 3 4
+ * }
+ *
+ * Assuming "ABC" is a Vector4 parameter, then the value + * "1 2 3 4" would be returned by this method. + *

+ * @return material parameter value as it would appear in a J3M file. + */ + public String getValueAsString() { + switch (type) { + case Boolean: + case Float: + case Int: + return value.toString(); + case Vector2: + Vector2f v2 = (Vector2f) value; + return v2.getX() + " " + v2.getY(); +/* +This may get used at a later point of time +When arrays can be inserted in J3M files + + case Vector2Array: + Vector2f[] v2Arr = (Vector2f[]) value; + String v2str = ""; + for (int i = 0; i < v2Arr.length ; i++) { + v2str += v2Arr[i].getX() + " " + v2Arr[i].getY() + "\n"; + } + return v2str; +*/ + case Vector3: + Vector3f v3 = (Vector3f) value; + return v3.getX() + " " + v3.getY() + " " + v3.getZ(); +/* + case Vector3Array: + Vector3f[] v3Arr = (Vector3f[]) value; + String v3str = ""; + for (int i = 0; i < v3Arr.length ; i++) { + v3str += v3Arr[i].getX() + " " + + v3Arr[i].getY() + " " + + v3Arr[i].getZ() + "\n"; + } + return v3str; + case Vector4Array: + // can be either ColorRGBA, Vector4f or Quaternion + if (value instanceof Vector4f) { + Vector4f[] v4arr = (Vector4f[]) value; + String v4str = ""; + for (int i = 0; i < v4arr.length ; i++) { + v4str += v4arr[i].getX() + " " + + v4arr[i].getY() + " " + + v4arr[i].getZ() + " " + + v4arr[i].getW() + "\n"; + } + return v4str; + } else if (value instanceof ColorRGBA) { + ColorRGBA[] colorArr = (ColorRGBA[]) value; + String colStr = ""; + for (int i = 0; i < colorArr.length ; i++) { + colStr += colorArr[i].getRed() + " " + + colorArr[i].getGreen() + " " + + colorArr[i].getBlue() + " " + + colorArr[i].getAlpha() + "\n"; + } + return colStr; + } else if (value instanceof Quaternion) { + Quaternion[] quatArr = (Quaternion[]) value; + String quatStr = ""; + for (int i = 0; i < quatArr.length ; i++) { + quatStr += quatArr[i].getX() + " " + + quatArr[i].getY() + " " + + quatArr[i].getZ() + " " + + quatArr[i].getW() + "\n"; + } + return quatStr; + } else { + throw new UnsupportedOperationException("Unexpected Vector4Array type: " + value); + } +*/ + case Vector4: + // can be either ColorRGBA, Vector4f or Quaternion + if (value instanceof Vector4f) { + Vector4f v4 = (Vector4f) value; + return v4.getX() + " " + v4.getY() + " " + + v4.getZ() + " " + v4.getW(); + } else if (value instanceof ColorRGBA) { + ColorRGBA color = (ColorRGBA) value; + return color.getRed() + " " + color.getGreen() + " " + + color.getBlue() + " " + color.getAlpha(); + } else if (value instanceof Quaternion) { + Quaternion quat = (Quaternion) value; + return quat.getX() + " " + quat.getY() + " " + + quat.getZ() + " " + quat.getW(); + } else { + throw new UnsupportedOperationException("Unexpected Vector4 type: " + value); + } + case Texture2D: + case Texture3D: + case TextureArray: + case TextureBuffer: + case TextureCubeMap: + Texture texVal = (Texture) value; + TextureKey texKey = (TextureKey) texVal.getKey(); + if (texKey == null){ + //throw new UnsupportedOperationException("The specified MatParam cannot be represented in J3M"); + // this is used in toString and the above line causes blender materials to throw this exception. + // toStrings should be very robust IMO as even debuggers often invoke toString and logging code + // often does as well, even implicitly. + return texVal+":returned null key"; + } + + String ret = ""; + if (texKey.isFlipY()) { + ret += "Flip "; + } + + //Wrap mode + ret += getWrapMode(texVal, Texture.WrapAxis.S); + ret += getWrapMode(texVal, Texture.WrapAxis.T); + ret += getWrapMode(texVal, Texture.WrapAxis.R); + + //Min and Mag filter + Texture.MinFilter def = Texture.MinFilter.BilinearNoMipMaps; + if(texVal.getImage().hasMipmaps() || texKey.isGenerateMips()){ + def = Texture.MinFilter.Trilinear; + } + if(texVal.getMinFilter() != def){ + ret += "Min" + texVal.getMinFilter().name()+ " "; + } + + if(texVal.getMagFilter() != Texture.MagFilter.Bilinear){ + ret += "Mag" + texVal.getMagFilter().name()+ " "; + } + + return ret + "\"" + texKey.getName() + "\""; + default: + return null; // parameter type not supported in J3M + } + } + + private String getWrapMode(Texture texVal, Texture.WrapAxis axis) { + WrapMode mode = WrapMode.EdgeClamp; + try{ + mode = texVal.getWrap(axis); + }catch (IllegalArgumentException e){ + //this axis doesn't exist on the texture + return ""; + } + if(mode != WrapMode.EdgeClamp){ + return"Wrap"+ mode.name() + "_" + axis.name() + " "; + } + return ""; + } + + @Override + public MatParam clone() { + try { + MatParam param = (MatParam) super.clone(); + return param; + } catch (CloneNotSupportedException ex) { + throw new AssertionError(); + } + } + + public void write(JmeExporter ex) throws IOException { + OutputCapsule oc = ex.getCapsule(this); + oc.write(type, "varType", null); + oc.write(name, "name", null); + if (value == null) { + } else if (value instanceof Savable) { + oc.write((Savable) value, "value_savable", null); + } else if (value instanceof Float) { + oc.write((Float) value, "value_float", 0f); + } else if (value instanceof Integer) { + oc.write((Integer) value, "value_int", 0); + } else if (value instanceof Boolean) { + oc.write((Boolean) value, "value_bool", false); + } else if (value.getClass().isArray() && value instanceof Savable[]) { + oc.write((Savable[]) value, "value_savable_array", null); + } + } + + public void read(JmeImporter im) throws IOException { + InputCapsule ic = im.getCapsule(this); + type = ic.readEnum("varType", VarType.class, null); + name = ic.readString("name", null); + prefixedName = "m_" + name; + switch (getVarType()) { + case Boolean: + value = ic.readBoolean("value_bool", false); + break; + case Float: + value = ic.readFloat("value_float", 0f); + break; + case Int: + value = ic.readInt("value_int", 0); + break; + case Vector2Array: + Savable[] savableArray = ic.readSavableArray("value_savable_array", null); + if (savableArray != null) { + value = new Vector2f[savableArray.length]; + System.arraycopy(savableArray, 0, value, 0, savableArray.length); + } + break; + case Vector3Array: + savableArray = ic.readSavableArray("value_savable_array", null); + if (savableArray != null) { + value = new Vector3f[savableArray.length]; + System.arraycopy(savableArray, 0, value, 0, savableArray.length); + } + break; + case Vector4Array: + savableArray = ic.readSavableArray("value_savable_array", null); + if (savableArray != null) { + value = new Vector4f[savableArray.length]; + System.arraycopy(savableArray, 0, value, 0, savableArray.length); + } + break; + case Matrix3Array: + savableArray = ic.readSavableArray("value_savable_array", null); + if (savableArray != null) { + value = new Matrix3f[savableArray.length]; + System.arraycopy(savableArray, 0, value, 0, savableArray.length); + } + break; + case Matrix4Array: + savableArray = ic.readSavableArray("value_savable_array", null); + if (savableArray != null) { + value = new Matrix4f[savableArray.length]; + System.arraycopy(savableArray, 0, value, 0, savableArray.length); + } + break; + default: + value = ic.readSavable("value_savable", null); + break; + } + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final MatParam other = (MatParam) obj; + if (this.type != other.type) { + return false; + } + if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) { + return false; + } + if (this.value != other.value && (this.value == null || !this.value.equals(other.value))) { + return false; + } + return true; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 59 * hash + (this.type != null ? this.type.hashCode() : 0); + hash = 59 * hash + (this.name != null ? this.name.hashCode() : 0); + hash = 59 * hash + (this.value != null ? this.value.hashCode() : 0); + return hash; + } + + @Override + public String toString() { + if (value != null) { + return type.name() + " " + name + " : " + getValueAsString(); + } else { + return type.name() + " " + name; + } + } +} diff --git a/src/main/java/com/jme3/scene/AssetLinkNode.java b/src/main/java/com/jme3/scene/AssetLinkNode.java new file mode 100644 index 00000000..82711ac6 --- /dev/null +++ b/src/main/java/com/jme3/scene/AssetLinkNode.java @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2009-2012 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.scene; + +import com.jme3.asset.AssetManager; +import com.jme3.asset.ModelKey; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import com.jme3.util.SafeArrayList; +import com.jme3.util.clone.Cloner; + +import java.io.IOException; +import java.util.*; +import java.util.Map.Entry; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * The AssetLinkNode does not store its children when exported to file. + * Instead, you can add a list of AssetKeys that will be loaded and attached + * when the AssetLinkNode is restored. + * + * @author normenhansen + */ +public class AssetLinkNode extends Node { + + protected ArrayList assetLoaderKeys = new ArrayList(); + protected Map assetChildren = new HashMap(); + + public AssetLinkNode() { + } + + public AssetLinkNode(ModelKey key) { + this(key.getName(), key); + } + + public AssetLinkNode(String name, ModelKey key) { + super(name); + assetLoaderKeys.add(key); + } + + /** + * Called internally by com.jme3.util.clone.Cloner. Do not call directly. + */ + @Override + public void cloneFields( Cloner cloner, Object original ) { + super.cloneFields(cloner, original); + + // This is a change in behavior because the old version did not clone + // this list... changes to one clone would be reflected in all. + // I think that's probably undesirable. -pspeed + this.assetLoaderKeys = cloner.clone(assetLoaderKeys); + this.assetChildren = new HashMap(); + } + + /** + * Add a "linked" child. These are loaded from the assetManager when the + * AssetLinkNode is loaded from a binary file. + * @param key + */ + public void addLinkedChild(ModelKey key) { + if (assetLoaderKeys.contains(key)) { + return; + } + assetLoaderKeys.add(key); + } + + public void removeLinkedChild(ModelKey key) { + assetLoaderKeys.remove(key); + } + + public ArrayList getAssetLoaderKeys() { + return assetLoaderKeys; + } + + public void attachLinkedChild(AssetManager manager, ModelKey key) { + addLinkedChild(key); + Spatial child = manager.loadAsset(key); + assetChildren.put(key, child); + attachChild(child); + } + + public void attachLinkedChild(Spatial spat, ModelKey key) { + addLinkedChild(key); + assetChildren.put(key, spat); + attachChild(spat); + } + + public void detachLinkedChild(ModelKey key) { + Spatial spatial = assetChildren.get(key); + if (spatial != null) { + detachChild(spatial); + } + removeLinkedChild(key); + assetChildren.remove(key); + } + + public void detachLinkedChild(Spatial child, ModelKey key) { + removeLinkedChild(key); + assetChildren.remove(key); + detachChild(child); + } + + /** + * Loads the linked children AssetKeys from the AssetManager and attaches them to the Node
+ * If they are already attached, they will be reloaded. + * @param manager + */ + public void attachLinkedChildren(AssetManager manager) { + detachLinkedChildren(); + for (Iterator it = assetLoaderKeys.iterator(); it.hasNext();) { + ModelKey assetKey = it.next(); + Spatial curChild = assetChildren.get(assetKey); + if (curChild != null) { + curChild.removeFromParent(); + } + Spatial child = manager.loadAsset(assetKey); + attachChild(child); + assetChildren.put(assetKey, child); + } + } + + public void detachLinkedChildren() { + Set> set = assetChildren.entrySet(); + for (Iterator> it = set.iterator(); it.hasNext();) { + Entry entry = it.next(); + entry.getValue().removeFromParent(); + it.remove(); + } + } + + @Override + public void read(JmeImporter e) throws IOException { + super.read(e); + + final InputCapsule capsule = e.getCapsule(this); + final AssetManager assetManager = e.getAssetManager(); + + assetLoaderKeys = (ArrayList) capsule.readSavableArrayList("assetLoaderKeyList", new ArrayList()); + + for (final Iterator iterator = assetLoaderKeys.iterator(); iterator.hasNext(); ) { + + final ModelKey modelKey = iterator.next(); + final Spatial child = assetManager.loadAsset(modelKey); + + if (child != null) { + child.parent = this; + children.add(child); + assetChildren.put(modelKey, child); + } else { + Logger.getLogger(this.getClass().getName()).log(Level.WARNING, + "Cannot locate {0} for asset link node {1}", new Object[]{modelKey, key}); + } + } + } + + @Override + public void write(JmeExporter e) throws IOException { + SafeArrayList childs = children; + children = new SafeArrayList<>(Spatial.class); + super.write(e); + OutputCapsule capsule = e.getCapsule(this); + capsule.writeSavableArrayList(assetLoaderKeys, "assetLoaderKeyList", null); + children = childs; + } +} diff --git a/src/main/java/com/jme3/shader/plugins/GLSLLoader.java b/src/main/java/com/jme3/shader/plugins/GLSLLoader.java new file mode 100644 index 00000000..184dcffd --- /dev/null +++ b/src/main/java/com/jme3/shader/plugins/GLSLLoader.java @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2009-2012 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.shader.plugins; + +import com.jme3.asset.*; +import com.jme3.asset.cache.AssetCache; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.*; + +/** + * GLSL File parser that supports #import pre-processor statement + */ +public class GLSLLoader implements AssetLoader { + + private AssetManager assetManager; + private Map dependCache = new HashMap<>(); + + /** + * Used to load {@link ShaderDependencyNode}s. + * Asset caching is disabled. + */ + private class ShaderDependencyKey extends AssetKey { + + public ShaderDependencyKey(String name) { + super(name); + } + + @Override + public Class getCacheType() { + // Disallow caching here + return null; + } + } + + /** + * Creates a {@link ShaderDependencyNode} from a stream representing shader code. + * + * @param reader the reader with shader code + * @param nodeName the node name. + * @return the shader dependency node + * @throws AssetLoadException if we failed to load the shader code. + */ + private ShaderDependencyNode loadNode(Reader reader, String nodeName) { + + ShaderDependencyNode node = new ShaderDependencyNode(nodeName); + StringBuilder sb = new StringBuilder(); + StringBuilder sbExt = new StringBuilder(); + + try (final BufferedReader bufferedReader = new BufferedReader(reader)) { + + String ln; + + if (!nodeName.equals("[main]")) { + sb.append("// -- begin import ").append(nodeName).append(" --\n"); + } + + while ((ln = bufferedReader.readLine()) != null) { + if (ln.trim().startsWith("#import ")) { + ln = ln.trim().substring(8).trim(); + if (ln.startsWith("\"") && ln.endsWith("\"") && ln.length() > 3) { + // import user code + // remove quotes to get filename + ln = ln.substring(1, ln.length() - 1); + if (ln.equals(nodeName)) { + throw new IOException("Node depends on itself."); + } + + // check cache first + ShaderDependencyNode dependNode = dependCache.get(ln); + + if (dependNode == null) { + Reader dependNodeReader = assetManager.loadAsset(new ShaderDependencyKey(ln)); + dependNode = loadNode(dependNodeReader, ln); + } + + node.addDependency(sb.length(), dependNode); + } + } else if (ln.trim().startsWith("#extension ")) { + sbExt.append(ln).append('\n'); + } else { + sb.append(ln).append('\n'); + } + } + if (!nodeName.equals("[main]")) { + sb.append("// -- end import ").append(nodeName).append(" --\n"); + } + } catch (final IOException ex) { + throw new AssetLoadException("Failed to load shader node: " + nodeName, ex); + } + + node.setSource(sb.toString()); + node.setExtensions(sbExt.toString()); + dependCache.put(nodeName, node); + return node; + } + + private ShaderDependencyNode nextIndependentNode() throws IOException { + Collection allNodes = dependCache.values(); + + if (allNodes == null || allNodes.isEmpty()) { + return null; + } + + for (ShaderDependencyNode node : allNodes) { + if (node.getDependOnMe().isEmpty()) { + return node; + } + } + + // Circular dependency found.. + for (ShaderDependencyNode node : allNodes){ + System.out.println(node.getName()); + } + + throw new IOException("Circular dependency."); + } + + private String resolveDependencies(ShaderDependencyNode node, Set alreadyInjectedSet, StringBuilder extensions) { + if (alreadyInjectedSet.contains(node)) { + return "// " + node.getName() + " was already injected at the top.\n"; + } else { + alreadyInjectedSet.add(node); + } + if (!node.getExtensions().isEmpty()) { + extensions.append(node.getExtensions()); + } + if (node.getDependencies().isEmpty()) { + return node.getSource(); + } else { + StringBuilder sb = new StringBuilder(node.getSource()); + List resolvedShaderNodes = new ArrayList(); + + for (ShaderDependencyNode dependencyNode : node.getDependencies()) { + resolvedShaderNodes.add(resolveDependencies(dependencyNode, alreadyInjectedSet, extensions)); + } + List injectIndices = node.getDependencyInjectIndices(); + for (int i = resolvedShaderNodes.size() - 1; i >= 0; i--) { + // Must insert them backwards .. + sb.insert(injectIndices.get(i), resolvedShaderNodes.get(i)); + } + return sb.toString(); + } + } + + @Override + public Object load(AssetInfo info) throws IOException { + // The input stream provided is for the vertex shader, + // to retrieve the fragment shader, use the content manager + this.assetManager = info.getManager(); + Reader reader = new InputStreamReader(info.openStream()); + if (info.getKey().getExtension().equals("glsllib")) { + // NOTE: Loopback, GLSLLIB is loaded by this loader + // and needs data as InputStream + return reader; + } else { + ShaderDependencyNode rootNode = loadNode(reader, "[main]"); + StringBuilder extensions = new StringBuilder(); + String code = resolveDependencies(rootNode, new HashSet<>(), extensions); + extensions.append(code); + dependCache.clear(); + return extensions.toString(); + } + } +} diff --git a/src/main/java/com/ss/editor/plugin/api/property/control/StringFromListPropertyEditorControl.java b/src/main/java/com/ss/editor/plugin/api/property/control/StringFromListPropertyEditorControl.java index 63902dec..166c4608 100644 --- a/src/main/java/com/ss/editor/plugin/api/property/control/StringFromListPropertyEditorControl.java +++ b/src/main/java/com/ss/editor/plugin/api/property/control/StringFromListPropertyEditorControl.java @@ -9,9 +9,6 @@ import com.ss.rlib.util.array.Array; import javafx.scene.control.ComboBox; import javafx.scene.control.SingleSelectionModel; -import javafx.scene.control.TextField; -import org.controlsfx.control.textfield.AutoCompletionBinding; -import org.controlsfx.control.textfield.TextFields; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -39,15 +36,16 @@ protected StringFromListPropertyEditorControl(@NotNull final VarTable vars, @Not setIgnoreListener(true); try { - comboBox.setEditable(true); + //FIXME need to find more userfriendly control + //comboBox.setEditable(true); - final TextField editor = comboBox.getEditor(); - final SingleSelectionModel selectionModel = comboBox.getSelectionModel(); - final AutoCompletionBinding binding = TextFields.bindAutoCompletion(editor, comboBox.getItems()); - binding.setOnAutoCompleted(event -> selectionModel.select(event.getCompletion())); + //final TextField editor = comboBox.getEditor(); + //final SingleSelectionModel selectionModel = comboBox.getSelectionModel(); + //final AutoCompletionBinding binding = TextFields.bindAutoCompletion(editor, comboBox.getItems()); + //binding.setOnAutoCompleted(event -> selectionModel.select(event.getCompletion())); - FXUtils.addClassesTo(editor, CSSClasses.TRANSPARENT_TEXT_FIELD, CSSClasses.TEXT_FIELD_IN_COMBO_BOX); - reload(); + //FXUtils.addClassesTo(editor, CSSClasses.TRANSPARENT_TEXT_FIELD, CSSClasses.TEXT_FIELD_IN_COMBO_BOX); + //reload(); } finally { setIgnoreListener(false); } From 810493cb5865897fcf2a52a8a755e2029c777915 Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Mon, 2 Oct 2017 07:25:26 +0300 Subject: [PATCH 49/50] updated dependencies. --- build.gradle | 4 ++-- gradle/wrapper/gradle-wrapper.jar | Bin 54712 -> 54708 bytes gradle/wrapper/gradle-wrapper.properties | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index ff7bbae6..e98fb8f0 100644 --- a/build.gradle +++ b/build.gradle @@ -125,7 +125,7 @@ dependencies { compile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.3' // extensions - compile ('com.github.JavaSaBr:jme3-spaceshift-extension:1.7.1') { + compile ('com.github.JavaSaBr:jme3-spaceshift-extension:1.7.2') { exclude group: 'org.jmonkeyengine' } compile ('com.github.JavaSaBr:tonegodemitter:2.4.0') { @@ -173,7 +173,7 @@ task javadocJar(type: Jar, dependsOn: javadoc) { } task wrapper(type: Wrapper) { - gradleVersion = '4.1' + gradleVersion = '4.2' } artifacts { diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index bc1c08bbf2bb03a13a7c3ab34ec4132f9fe8570b..7a3265ee94c0ab25cf079ac8ccdf87f41d455d42 100644 GIT binary patch delta 673 zcmYL{T}YEr9LC@CS2iPM?-E7dDatC-SEkOe=9HWi7A|cvi!7}mR_kk$kr%=)BqA>g zwTIC+k+YNsInN&s|1%lPmm+E8Eh%)}BF_bAu%@Q_3^@dEz<l3g#bM?1)#;Dy%C z*08Rfp_Kwr&n_$#^@k<$_J3tb8p5lYQ&ern_>2i&vk|sgm5ME*;j$Po#HGGU$o4ut zVzaems!Pipl+CHkkB+&cBs+!K)|La8vj8h|Dk*JgD;Jh|m0CK5^vxfpvMzy<`9q9g zTsTe!4=kSDxVNA(A0`(X>6r&_J$qqD%BYVhRPN=n5NEx+gf+&k=v55*itr`UOy_+Z zD@AfLs!*sG-+Ig8NXU4}<)fRpKDZOVqE@=yFHq}OaQd@KrU4Wmd>#aSeGwT?5UXcak+u^JQr9RKSG`{j%M7A*Dx9H<8Jl?#zYtUjVpqWOon)o1DZFX^1&JE}ZvX%Q delta 808 zcmYk4Ur3Wt7{fZq?lUnynOJ`$WrPWw}uML!+fEw9<&`LNlbY1icU>qzfrU z&4;o~k-YFm2!shjfyy?m%{Bkb5bC-lZ?Yn~=%TZu=FZjgyubH-&pGcozR{$K(WIWE z+LfG+rcz0gE#bq7!!utFV7Wm7N(3)vd(tJ6^vRA?^)3z<;HWK42frHi96^r_Q4S{;F-;!=eusM$2I|PWT`P}+l|k& z3Ke8+hRk>#F(5ZDG2fOaV0m5|+Icfmej?Q73s{)H0UgdnnCTSoyfZ~qcDNJa1Gj*$ z-Fh^NZBI!pFyA>V~glj^FF+2LX`)*`ny z2am!Ey9Zy`#fGpRzQz=I=WbE-HeNMb@o}z{YVQj9H)ca+M5bVau$_@3^smG5dLs6M7wuw-0TMq6l8ZZ)-*-uPI>&et9 zfOq~3nY&TYU5p6lU*=_|16!ye%Tpe7pl6|oOnpLD_31@hRe$2J{=5*ds6U-=pwQxg zXqz@@ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9e37093f..52dd1f04 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Thu Aug 10 08:06:48 MSK 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.2-bin.zip From 5cbf5a884d9e261e68af28c6830eab8bb6a7b940 Mon Sep 17 00:00:00 2001 From: JavaSaBr Date: Mon, 2 Oct 2017 09:25:45 +0300 Subject: [PATCH 50/50] updated readme. --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index cc189d92..b1ff3ec8 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,14 @@ ## [Video about this editor](https://youtu.be/5nX8HXYdENU) ## +## ver. 1.2.0 ## +* -Fixed the far view plane distance of the editor camera. +* -Updated tonegod.emitter library. +* -Added the new plugin Shader Node Tools. +* -Updated jME libraries. +* -Updated Tree Generator plugin. +* -Fixed some bugs. + ## ver. 1.1.0 ## * -Added the new plugin to generate trees. * -Updated the Material Editor.