├── .editorconfig ├── .gitattributes ├── .github └── workflows │ └── release.yml ├── .gitignore ├── CHANGES.md ├── KNOWN_ISSUES.md ├── LICENSE ├── README.md ├── assets ├── .gitkeep ├── blank.p ├── default │ ├── default.atlas │ └── default.png ├── demolition.p ├── explosion.p ├── firework.p ├── flame.p ├── glitch.p ├── infinity.p ├── laser.p ├── logo.png ├── particle-ball1.png ├── particle-ball2.png ├── particle-ball3.png ├── particle-ball4.png ├── particle-cloud.png ├── particle-fire.png ├── particle-star.png ├── particle.png ├── pre_particle.png ├── rain.p ├── skin │ ├── bold.fnt │ ├── boldx1_5.fnt │ ├── boldx2.fnt │ ├── boldx3.fnt │ ├── boldx4.fnt │ ├── font-black.fnt │ ├── font-blackx1_5.fnt │ ├── font-blackx2.fnt │ ├── font-blackx3.fnt │ ├── font-blackx4.fnt │ ├── font.fnt │ ├── fontx1_5.fnt │ ├── fontx2.fnt │ ├── fontx3.fnt │ ├── fontx4.fnt │ ├── gdxparticleeditor.atlas │ ├── gdxparticleeditor.json │ ├── gdxparticleeditor.png │ ├── header.fnt │ ├── headerx1_5.fnt │ ├── headerx2.fnt │ ├── headerx3.fnt │ ├── headerx4.fnt │ ├── high-contrast.fnt │ ├── high-contrastx1_5.fnt │ ├── high-contrastx2.fnt │ ├── high-contrastx3.fnt │ └── high-contrastx4.fnt ├── smoke.p ├── sparks.p ├── splash.p ├── thruster.p └── trail.p ├── build.gradle ├── core ├── build.gradle ├── icons │ ├── logo.icns │ ├── logo.ico │ └── logo.png └── src │ └── main │ ├── java │ └── com │ │ ├── badlogic │ │ └── gdx │ │ │ ├── backends │ │ │ └── lwjgl3 │ │ │ │ └── DefaultLwjgl3Input.java │ │ │ └── scenes │ │ │ └── scene2d │ │ │ ├── ui │ │ │ ├── ScrollPane.java │ │ │ └── TextField.java │ │ │ └── utils │ │ │ ├── DragAndDrop.java │ │ │ └── DragListener.java │ │ └── ray3k │ │ ├── gdxparticleeditor │ │ ├── Core.java │ │ ├── FileDialogs.java │ │ ├── Listeners.java │ │ ├── ParticlePreview.java │ │ ├── PresetActions.java │ │ ├── PreviewSettings.java │ │ ├── Settings.java │ │ ├── SkinLoader.java │ │ ├── SplitPaneSystemCursorListener.java │ │ ├── SystemCursorListener.java │ │ ├── TextFileApplicationLogger.java │ │ ├── Utils.java │ │ ├── lwjgl3 │ │ │ ├── Lwjgl3Launcher.java │ │ │ └── StartupHelper.java │ │ ├── runnables │ │ │ ├── ExportRunnable.java │ │ │ ├── ImagesRunnable.java │ │ │ ├── MergeRunnable.java │ │ │ ├── OpenRunnable.java │ │ │ ├── RedoShortcutRunnable.java │ │ │ ├── ReloadImagesRunnable.java │ │ │ ├── SaveAsRunnable.java │ │ │ ├── SaveRunnable.java │ │ │ ├── SwitchModeRunnable.java │ │ │ └── UndoShortcutRunnable.java │ │ ├── shortcuts │ │ │ ├── InvalidShortcutException.java │ │ │ ├── KeyMap.java │ │ │ ├── KeybindReference.java │ │ │ ├── Shortcut.java │ │ │ └── ShortcutManager.java │ │ ├── undo │ │ │ ├── UndoManager.java │ │ │ ├── Undoable.java │ │ │ └── undoables │ │ │ │ ├── CountMaxUndoable.java │ │ │ │ ├── CountMinUndoable.java │ │ │ │ ├── DeleteEmitterUndoable.java │ │ │ │ ├── DualScaledNumericValueRelativeUndoable.java │ │ │ │ ├── DualScaledNumericValueUndoable.java │ │ │ │ ├── ImagesAddUndoable.java │ │ │ │ ├── ImagesMoveUndoable.java │ │ │ │ ├── ImagesRemoveUndoable.java │ │ │ │ ├── ImagesSpriteModeUndoable.java │ │ │ │ ├── MergeEmitterUndoable.java │ │ │ │ ├── MoveEmitterUndoable.java │ │ │ │ ├── NewEmitterUndoable.java │ │ │ │ ├── OptionsUndoable.java │ │ │ │ ├── RangedNumericValueUndoable.java │ │ │ │ ├── RenameEmitterUndoable.java │ │ │ │ ├── ScaledNumericValueIndependentUndoable.java │ │ │ │ ├── ScaledNumericValueRelativeUndoable.java │ │ │ │ ├── ScaledNumericValueUndoable.java │ │ │ │ ├── SetPropertyUndoable.java │ │ │ │ ├── SpawnEdgesUndoable.java │ │ │ │ ├── SpawnSideUndoable.java │ │ │ │ ├── SpawnTypeUndoable.java │ │ │ │ └── TintUndoable.java │ │ └── widgets │ │ │ ├── CardGroup.java │ │ │ ├── Carousel.java │ │ │ ├── CollapsibleGroup.java │ │ │ ├── ColorGraph.java │ │ │ ├── EditableLabel.java │ │ │ ├── InfSlider.java │ │ │ ├── LeadingTruncateLabel.java │ │ │ ├── LineGraph.java │ │ │ ├── NoCaptureKeyboardFocusListener.java │ │ │ ├── Panel.java │ │ │ ├── Toast.java │ │ │ ├── ToggleGroup.java │ │ │ ├── WelcomeCard.java │ │ │ ├── panels │ │ │ ├── EffectEmittersPanel.java │ │ │ ├── EmitterPropertiesPanel.java │ │ │ ├── PreviewPanel.java │ │ │ ├── StartPanel.java │ │ │ └── SummaryPanel.java │ │ │ ├── poptables │ │ │ ├── PopAddProperty.java │ │ │ ├── PopConfirmClose.java │ │ │ ├── PopConfirmLoad.java │ │ │ ├── PopEditorSettings.java │ │ │ ├── PopError.java │ │ │ ├── PopExport.java │ │ │ ├── PopImageError.java │ │ │ ├── PopLocateImages.java │ │ │ ├── PopPreviewSettings.java │ │ │ └── PopTemplate.java │ │ │ ├── styles │ │ │ ├── PPcolorGraphStyle.java │ │ │ ├── PPcolorPickerStyle.java │ │ │ ├── PPdraggableListStyle.java │ │ │ ├── PPdraggableTextListNoBGStyle.java │ │ │ ├── PPdraggableTextListStyle.java │ │ │ ├── PPeditableLabelStyle.java │ │ │ ├── PPinfSliderStyle.java │ │ │ ├── PPlineGraphBigStyle.java │ │ │ ├── PPlineGraphStyle.java │ │ │ ├── PPresizeWidgetStyle.java │ │ │ ├── PPspinnerStyle.java │ │ │ └── Styles.java │ │ │ ├── subpanels │ │ │ ├── CountSubPanel.java │ │ │ ├── GraphSubPanel.java │ │ │ ├── ImagesSubPanel.java │ │ │ ├── OptionsSubPanel.java │ │ │ ├── RangeSubPanel.java │ │ │ ├── SizeSubPanel.java │ │ │ ├── SpawnSubPanel.java │ │ │ ├── TintSubPanel.java │ │ │ └── TransparencySubPanel.java │ │ │ └── tables │ │ │ ├── ClassicTable.java │ │ │ ├── WelcomeTable.java │ │ │ └── WizardTable.java │ │ └── stripe │ │ ├── DraggableList.java │ │ ├── DraggableTextList.java │ │ ├── PopColorPicker.java │ │ ├── PopTable.java │ │ ├── PopTableHoverListener.java │ │ ├── ResizeWidget.java │ │ ├── ScrollFocusListener.java │ │ ├── Spinner.java │ │ └── ViewportWidget.java │ └── resources │ ├── icon128.png │ ├── icon16.png │ ├── icon32.png │ ├── icon64.png │ └── version ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.{java,scala,groovy,kt,kts}] 12 | indent_size = 4 13 | 14 | [*.gradle] 15 | indent_size = 2 16 | 17 | [*.md] 18 | trim_trailing_whitespace = false 19 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | *.bat text=auto eol=crlf 3 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Build GDX Particle Editor and create a draft release 2 | 3 | on: 4 | create 5 | 6 | jobs: 7 | jar: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | 12 | - name: Set up JDK 14 13 | uses: actions/setup-java@v1 14 | with: 15 | java-version: 14 16 | 17 | - name: Fix gradlew file permissions 18 | run: chmod +x gradlew 19 | 20 | - name: Build jar 21 | run: ./gradlew core:jar 22 | 23 | - name: Upload jar artifact 24 | uses: actions/upload-artifact@v4 25 | with: 26 | name: gdx-particle-editor.jar 27 | path: core/build/lib/gdx-particle-editor.jar 28 | 29 | release: 30 | permissions: write-all 31 | needs: [jar] 32 | runs-on: ubuntu-latest 33 | steps: 34 | - name: Download all artifacts 35 | uses: actions/download-artifact@v4 36 | 37 | - name: Display structure of downloaded files 38 | run: ls -R 39 | 40 | - name: Create Release 41 | id: create_release 42 | uses: actions/create-release@v1 43 | env: 44 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 45 | with: 46 | tag_name: ${{ github.ref }} 47 | release_name: GDX Particle Editor ver. ${{ github.ref }} 48 | draft: true 49 | prerelease: false 50 | 51 | - name: Upload jar release artifacts 52 | uses: actions/upload-release-asset@v1 53 | env: 54 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 55 | with: 56 | upload_url: ${{ steps.create_release.outputs.upload_url }} 57 | asset_path: gdx-particle-editor.jar/gdx-particle-editor.jar 58 | asset_name: gdx-particle-editor.jar 59 | asset_content_type: application/x-java-archive 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## App: 2 | /design/ 3 | 4 | ## Gradle: 5 | .gradle/ 6 | gradle-app.setting 7 | /build/ 8 | /android/build/ 9 | /core/build/ 10 | /lwjgl2/build/ 11 | /lwjgl3/build/ 12 | /html/build/ 13 | /teavm/build/ 14 | /ios/build/ 15 | /ios-moe/build/ 16 | /headless/build/ 17 | /server/build/ 18 | /shared/build/ 19 | 20 | ## Java: 21 | *.class 22 | *.war 23 | *.ear 24 | hs_err_pid* 25 | .attach_pid* 26 | 27 | ## Android: 28 | /android/libs/armeabi-v7a/ 29 | /android/libs/arm64-v8a/ 30 | /android/libs/x86/ 31 | /android/libs/x86_64/ 32 | /android/gen/ 33 | /android/out/ 34 | local.properties 35 | com_crashlytics_export_strings.xml 36 | 37 | ## Robovm: 38 | /ios/robovm-build/ 39 | 40 | ## iOS: 41 | /ios/xcode/*.xcodeproj/* 42 | !/ios/xcode/*.xcodeproj/xcshareddata 43 | !/ios/xcode/*.xcodeproj/project.pbxproj 44 | /ios/xcode/native/ 45 | /ios/IOSLauncher.app 46 | /ios/IOSLauncher.app.dSYM 47 | 48 | ## GWT: 49 | /html/war/ 50 | /html/gwt-unitCache/ 51 | .apt_generated/ 52 | /html/war/WEB-INF/deploy/ 53 | /html/war/WEB-INF/classes/ 54 | .gwt/ 55 | gwt-unitCache/ 56 | www-test/ 57 | .gwt-tmp/ 58 | 59 | ## TeaVM: 60 | # Not sure yet... 61 | 62 | ## IntelliJ, Android Studio: 63 | .idea/ 64 | *.ipr 65 | *.iws 66 | *.iml 67 | 68 | ## Eclipse: 69 | .classpath 70 | .project 71 | .metadata/ 72 | /android/bin/ 73 | /core/bin/ 74 | /lwjgl2/bin/ 75 | /lwjgl3/bin/ 76 | /html/bin/ 77 | /teavm/bin/ 78 | /ios/bin/ 79 | /ios-moe/bin/ 80 | /headless/bin/ 81 | /server/bin/ 82 | /shared/bin/ 83 | *.tmp 84 | *.bak 85 | *.swp 86 | *~.nib 87 | .settings/ 88 | .loadpath 89 | .externalToolBuilders/ 90 | *.launch 91 | 92 | 93 | ## NetBeans: 94 | 95 | /nbproject/private/ 96 | /android/nbproject/private/ 97 | /core/nbproject/private/ 98 | /lwjgl2/nbproject/private/ 99 | /lwjgl3/nbproject/private/ 100 | /html/nbproject/private/ 101 | /teavm/nbproject/private/ 102 | /ios/nbproject/private/ 103 | /ios-moe/nbproject/private/ 104 | /headless/nbproject/private/ 105 | /server/nbproject/private/ 106 | /shared/nbproject/private/ 107 | 108 | /nbbuild/ 109 | /android/nbbuild/ 110 | /core/nbbuild/ 111 | /lwjgl2/nbbuild/ 112 | /lwjgl3/nbbuild/ 113 | /html/nbbuild/ 114 | /teavm/nbbuild/ 115 | /ios/nbbuild/ 116 | /ios-moe/nbbuild/ 117 | /headless/nbbuild/ 118 | /server/nbbuild/ 119 | /shared/nbbuild/ 120 | 121 | /dist/ 122 | /android/dist/ 123 | /core/dist/ 124 | /lwjgl2/dist/ 125 | /lwjgl3/dist/ 126 | /html/dist/ 127 | /teavm/dist/ 128 | /ios/dist/ 129 | /ios-moe/dist/ 130 | /headless/dist/ 131 | /server/dist/ 132 | /shared/dist/ 133 | 134 | /nbdist/ 135 | /android/nbdist/ 136 | /core/nbdist/ 137 | /lwjgl2/nbdist/ 138 | /lwjgl3/nbdist/ 139 | /html/nbdist/ 140 | /teavm/nbdist/ 141 | /ios/nbdist/ 142 | /ios-moe/nbdist/ 143 | /headless/nbdist/ 144 | /server/nbdist/ 145 | /shared/nbdist/ 146 | 147 | nbactions.xml 148 | nb-configuration.xml 149 | 150 | ## OS-Specific: 151 | .DS_Store 152 | Thumbs.db 153 | 154 | ## Miscellaneous: 155 | *~ 156 | *.*# 157 | *#*# 158 | -------------------------------------------------------------------------------- /KNOWN_ISSUES.md: -------------------------------------------------------------------------------- 1 | # Known Issues 2 | * YOUTUBE button does nothing. I haven't made the videos yet. 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GDX Particle Editor 2 | [![License](https://img.shields.io/github/license/libgdx/gdx-particle-editor)](https://github.com/libgdx/gdx-particle-editor/blob/master/LICENSE) 3 | [![Downloads](https://img.shields.io/github/v/release/libgdx/gdx-particle-editor)](https://github.com/libgdx/gdx-particle-editor/releases) 4 | 5 | [](https://github.com/libgdx/gdx-particle-editor/releases) 6 | 7 | ## Purpose 8 | 9 | The GDX Particle Editor is now the official particle editor for libGDX. These are the goals of the project: 10 | 11 | * Target all desktop platforms including M1 Macs. 12 | * Disallow any use of Swing, AWT, or similar toolkits. GDX Particle Editor is solely a Scene2D.UI project. 13 | * Improve the workflow and interface for maximum efficiency and attractiveness. 14 | * Export libGDX compatible particle effects. Extra features requiring a separate runtime library will not be supported. 15 | * Include all features available in the original [Particle Editor](https://github.com/libgdx/libgdx/tree/master/extensions/gdx-tools/src/com/badlogic/gdx/tools/particleeditor). 16 | 17 | ## How to run 18 | 19 | * Download the latest release from here: [releases](https://github.com/libgdx/gdx-particle-editor/releases) 20 | * Ensure you have at least JDK 11 installed on your system: [download](https://bell-sw.com/pages/downloads/) 21 | * Run the jar file: `java -jar gdx-particle-editor.jar` 22 | 23 | ## How to create particles 24 | 25 | Please read the [wiki](https://github.com/libgdx/gdx-particle-editor/wiki) to learn about creating particle effects and running GDX Particle Editor. 26 | 27 | ## How to contribute 28 | 29 | This project is maintained by Raymond "Raeleus" Buckley. 30 | * You may create an [issue](https://github.com/libgdx/gdx-particle-editor/issues) in GitHub if you encountered a crash or have a feature request. Please include a crash log. This is typically found in the user path ex. `C:\Users\\.gdxparticleeditor\` 31 | * See [CHANGES](https://github.com/libgdx/gdx-particle-editor/blob/master/CHANGES.md) and [KNOWN_ISSUES](https://github.com/libgdx/gdx-particle-editor/blob/master/KNOWN_ISSUES.md) for more details on development 32 | * Please target the `dev` branch for all PR's 33 | -------------------------------------------------------------------------------- /assets/.gitkeep: -------------------------------------------------------------------------------- 1 | This file can be deleted if there are any other files present in this folder. 2 | It is only here to ensure this folder is added to Git, instead of being 3 | ignored because it is empty. -------------------------------------------------------------------------------- /assets/blank.p: -------------------------------------------------------------------------------- 1 | Untitled 2 | - Delay - 3 | active: false 4 | - Duration - 5 | lowMin: 0.0 6 | lowMax: 0.0 7 | - Count - 8 | min: 0 9 | max: 0 10 | - Emission - 11 | lowMin: 0.0 12 | lowMax: 0.0 13 | highMin: 0.0 14 | highMax: 0.0 15 | relative: false 16 | scalingCount: 1 17 | scaling0: 1.0 18 | timelineCount: 1 19 | timeline0: 0.0 20 | - Life - 21 | lowMin: 0.0 22 | lowMax: 0.0 23 | highMin: 0.0 24 | highMax: 0.0 25 | relative: false 26 | scalingCount: 1 27 | scaling0: 1.0 28 | timelineCount: 1 29 | timeline0: 0.0 30 | independent: false 31 | - Life Offset - 32 | active: false 33 | independent: false 34 | - X Offset - 35 | active: false 36 | - Y Offset - 37 | active: false 38 | - Spawn Shape - 39 | shape: point 40 | - Spawn Width - 41 | lowMin: 0.0 42 | lowMax: 0.0 43 | highMin: 0.0 44 | highMax: 0.0 45 | relative: false 46 | scalingCount: 1 47 | scaling0: 1.0 48 | timelineCount: 1 49 | timeline0: 0.0 50 | - Spawn Height - 51 | lowMin: 0.0 52 | lowMax: 0.0 53 | highMin: 0.0 54 | highMax: 0.0 55 | relative: false 56 | scalingCount: 1 57 | scaling0: 1.0 58 | timelineCount: 1 59 | timeline0: 0.0 60 | - X Scale - 61 | lowMin: 0.0 62 | lowMax: 0.0 63 | highMin: 0.0 64 | highMax: 0.0 65 | relative: false 66 | scalingCount: 1 67 | scaling0: 1.0 68 | timelineCount: 1 69 | timeline0: 0.0 70 | - Y Scale - 71 | active: false 72 | - Velocity - 73 | active: false 74 | - Angle - 75 | active: false 76 | - Rotation - 77 | active: false 78 | - Wind - 79 | active: false 80 | - Gravity - 81 | active: false 82 | - Tint - 83 | colorsCount: 3 84 | colors0: 1.0 85 | colors1: 1.0 86 | colors2: 1.0 87 | timelineCount: 1 88 | timeline0: 0.0 89 | - Transparency - 90 | lowMin: 0.0 91 | lowMax: 0.0 92 | highMin: 1.0 93 | highMax: 1.0 94 | relative: false 95 | scalingCount: 1 96 | scaling0: 1.0 97 | timelineCount: 1 98 | timeline0: 0.0 99 | - Options - 100 | attached: false 101 | continuous: true 102 | aligned: false 103 | additive: false 104 | behind: false 105 | premultipliedAlpha: false 106 | spriteMode: single 107 | - Image Paths - 108 | particle.png 109 | 110 | -------------------------------------------------------------------------------- /assets/default/default.atlas: -------------------------------------------------------------------------------- 1 | default.png 2 | size:512,512 3 | filter:Linear,Linear 4 | logo 5 | bounds:1,457,300,50 6 | particle 7 | bounds:303,475,32,32 8 | particle-ball1 9 | bounds:1,327,128,128 10 | particle-ball2 11 | bounds:1,197,128,128 12 | particle-ball3 13 | bounds:131,327,128,128 14 | particle-ball4 15 | bounds:1,67,128,128 16 | particle-cloud 17 | bounds:131,197,128,128 18 | particle-fire 19 | bounds:131,67,128,128 20 | particle-star 21 | bounds:1,1,64,64 22 | pre_particle 23 | bounds:261,423,32,32 24 | -------------------------------------------------------------------------------- /assets/default/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libgdx/gdx-particle-editor/9b0aa9c8a2208c510a9d8a6b26c08f80b6aaf7dc/assets/default/default.png -------------------------------------------------------------------------------- /assets/demolition.p: -------------------------------------------------------------------------------- 1 | Demolition 2 | - Delay - 3 | active: false 4 | - Duration - 5 | lowMin: 1000.0 6 | lowMax: 1000.0 7 | - Count - 8 | min: 0 9 | max: 200 10 | - Emission - 11 | lowMin: 0.0 12 | lowMax: 0.0 13 | highMin: 50.0 14 | highMax: 50.0 15 | relative: false 16 | scalingCount: 1 17 | scaling0: 1.0 18 | timelineCount: 1 19 | timeline0: 0.0 20 | - Life - 21 | lowMin: 0.0 22 | lowMax: 0.0 23 | highMin: 200.0 24 | highMax: 200.0 25 | relative: false 26 | scalingCount: 1 27 | scaling0: 1.0 28 | timelineCount: 1 29 | timeline0: 0.0 30 | independent: false 31 | - Life Offset - 32 | active: false 33 | independent: false 34 | - X Offset - 35 | active: false 36 | - Y Offset - 37 | active: false 38 | - Spawn Shape - 39 | shape: ellipse 40 | edges: false 41 | side: both 42 | - Spawn Width - 43 | lowMin: 0.0 44 | lowMax: 0.0 45 | highMin: 100.0 46 | highMax: 100.0 47 | relative: false 48 | scalingCount: 1 49 | scaling0: 1.0 50 | timelineCount: 1 51 | timeline0: 0.0 52 | - Spawn Height - 53 | lowMin: 0.0 54 | lowMax: 0.0 55 | highMin: 100.0 56 | highMax: 100.0 57 | relative: false 58 | scalingCount: 1 59 | scaling0: 1.0 60 | timelineCount: 1 61 | timeline0: 0.0 62 | - X Scale - 63 | lowMin: 0.0 64 | lowMax: 0.0 65 | highMin: 30.0 66 | highMax: 60.0 67 | relative: false 68 | scalingCount: 1 69 | scaling0: 1.0 70 | timelineCount: 1 71 | timeline0: 0.0 72 | - Y Scale - 73 | active: false 74 | - Velocity - 75 | active: true 76 | lowMin: 0.0 77 | lowMax: 0.0 78 | highMin: 0.0 79 | highMax: 0.0 80 | relative: false 81 | scalingCount: 1 82 | scaling0: 1.0 83 | timelineCount: 1 84 | timeline0: 0.0 85 | - Angle - 86 | active: true 87 | lowMin: 0.0 88 | lowMax: 0.0 89 | highMin: 0.0 90 | highMax: 360.0 91 | relative: false 92 | scalingCount: 1 93 | scaling0: 1.0 94 | timelineCount: 1 95 | timeline0: 0.0 96 | - Rotation - 97 | active: true 98 | lowMin: 0.0 99 | lowMax: 0.0 100 | highMin: 0.0 101 | highMax: 360.0 102 | relative: false 103 | scalingCount: 1 104 | scaling0: 1.0 105 | timelineCount: 1 106 | timeline0: 0.0 107 | - Wind - 108 | active: false 109 | - Gravity - 110 | active: false 111 | - Tint - 112 | colorsCount: 9 113 | colors0: 1.0 114 | colors1: 1.0 115 | colors2: 1.0 116 | colors3: 0.91764706 117 | colors4: 1.0 118 | colors5: 0.0 119 | colors6: 1.0 120 | colors7: 0.0 121 | colors8: 0.0 122 | timelineCount: 3 123 | timeline0: 0.0 124 | timeline1: 0.10000006 125 | timeline2: 1.0 126 | - Transparency - 127 | lowMin: 0.0 128 | lowMax: 0.0 129 | highMin: 1.0 130 | highMax: 1.0 131 | relative: false 132 | scalingCount: 1 133 | scaling0: 1.0 134 | timelineCount: 1 135 | timeline0: 0.0 136 | - Options - 137 | attached: false 138 | continuous: true 139 | aligned: false 140 | additive: false 141 | behind: false 142 | premultipliedAlpha: false 143 | spriteMode: animated 144 | - Image Paths - 145 | particle-ball1.png 146 | particle-ball2.png 147 | particle-ball3.png 148 | particle-ball4.png 149 | 150 | -------------------------------------------------------------------------------- /assets/flame.p: -------------------------------------------------------------------------------- 1 | Flame 2 | - Delay - 3 | active: false 4 | - Duration - 5 | lowMin: 3000.0 6 | lowMax: 3000.0 7 | - Count - 8 | min: 0 9 | max: 200 10 | - Emission - 11 | lowMin: 0.0 12 | lowMax: 0.0 13 | highMin: 250.0 14 | highMax: 250.0 15 | relative: false 16 | scalingCount: 1 17 | scaling0: 1.0 18 | timelineCount: 1 19 | timeline0: 0.0 20 | - Life - 21 | lowMin: 0.0 22 | lowMax: 0.0 23 | highMin: 500.0 24 | highMax: 1000.0 25 | relative: false 26 | scalingCount: 3 27 | scaling0: 1.0 28 | scaling1: 1.0 29 | scaling2: 0.3 30 | timelineCount: 3 31 | timeline0: 0.0 32 | timeline1: 0.66 33 | timeline2: 1.0 34 | independent: false 35 | - Life Offset - 36 | active: false 37 | independent: false 38 | - X Offset - 39 | active: false 40 | - Y Offset - 41 | active: false 42 | - Spawn Shape - 43 | shape: point 44 | - Spawn Width - 45 | lowMin: 0.0 46 | lowMax: 0.0 47 | highMin: 0.0 48 | highMax: 0.0 49 | relative: false 50 | scalingCount: 1 51 | scaling0: 1.0 52 | timelineCount: 1 53 | timeline0: 0.0 54 | - Spawn Height - 55 | lowMin: 0.0 56 | lowMax: 0.0 57 | highMin: 0.0 58 | highMax: 0.0 59 | relative: false 60 | scalingCount: 1 61 | scaling0: 1.0 62 | timelineCount: 1 63 | timeline0: 0.0 64 | - X Scale - 65 | lowMin: 0.0 66 | lowMax: 0.0 67 | highMin: 32.0 68 | highMax: 32.0 69 | relative: false 70 | scalingCount: 1 71 | scaling0: 1.0 72 | timelineCount: 1 73 | timeline0: 0.0 74 | - Y Scale - 75 | active: false 76 | - Velocity - 77 | active: true 78 | lowMin: 0.0 79 | lowMax: 0.0 80 | highMin: 30.0 81 | highMax: 300.0 82 | relative: false 83 | scalingCount: 1 84 | scaling0: 1.0 85 | timelineCount: 1 86 | timeline0: 0.0 87 | - Angle - 88 | active: true 89 | lowMin: 90.0 90 | lowMax: 90.0 91 | highMin: 45.0 92 | highMax: 135.0 93 | relative: false 94 | scalingCount: 3 95 | scaling0: 1.0 96 | scaling1: 0.0 97 | scaling2: 0.0 98 | timelineCount: 3 99 | timeline0: 0.0 100 | timeline1: 0.5 101 | timeline2: 1.0 102 | - Rotation - 103 | active: false 104 | - Wind - 105 | active: false 106 | - Gravity - 107 | active: false 108 | - Tint - 109 | colorsCount: 3 110 | colors0: 1.0 111 | colors1: 0.12156863 112 | colors2: 0.047058824 113 | timelineCount: 1 114 | timeline0: 0.0 115 | - Transparency - 116 | lowMin: 0.0 117 | lowMax: 0.0 118 | highMin: 1.0 119 | highMax: 1.0 120 | relative: false 121 | scalingCount: 4 122 | scaling0: 0.0 123 | scaling1: 1.0 124 | scaling2: 0.75 125 | scaling3: 0.0 126 | timelineCount: 4 127 | timeline0: 0.0 128 | timeline1: 0.2 129 | timeline2: 0.8 130 | timeline3: 1.0 131 | - Options - 132 | attached: false 133 | continuous: true 134 | aligned: false 135 | additive: true 136 | behind: false 137 | premultipliedAlpha: false 138 | spriteMode: single 139 | - Image Paths - 140 | particle.png 141 | 142 | -------------------------------------------------------------------------------- /assets/glitch.p: -------------------------------------------------------------------------------- 1 | Glitch 2 | - Delay - 3 | active: false 4 | - Duration - 5 | lowMin: 10000.0 6 | lowMax: 10000.0 7 | - Count - 8 | min: 0 9 | max: 200 10 | - Emission - 11 | lowMin: 0.0 12 | lowMax: 0.0 13 | highMin: 14.0 14 | highMax: 14.0 15 | relative: false 16 | scalingCount: 1 17 | scaling0: 1.0 18 | timelineCount: 1 19 | timeline0: 0.0 20 | - Life - 21 | lowMin: 0.0 22 | lowMax: 0.0 23 | highMin: 500.0 24 | highMax: 500.0 25 | relative: false 26 | scalingCount: 1 27 | scaling0: 1.0 28 | timelineCount: 1 29 | timeline0: 0.0 30 | independent: false 31 | - Life Offset - 32 | active: false 33 | independent: false 34 | - X Offset - 35 | active: true 36 | lowMin: -3.0 37 | lowMax: 3.0 38 | highMin: 0.0 39 | highMax: 0.0 40 | relative: false 41 | scalingCount: 1 42 | scaling0: 1.0 43 | timelineCount: 1 44 | timeline0: 0.0 45 | - Y Offset - 46 | active: true 47 | lowMin: -3.0 48 | lowMax: 3.0 49 | highMin: 0.0 50 | highMax: 0.0 51 | relative: false 52 | scalingCount: 1 53 | scaling0: 1.0 54 | timelineCount: 1 55 | timeline0: 0.0 56 | - Spawn Shape - 57 | shape: point 58 | - Spawn Width - 59 | lowMin: 0.0 60 | lowMax: 0.0 61 | highMin: 0.0 62 | highMax: 0.0 63 | relative: false 64 | scalingCount: 1 65 | scaling0: 1.0 66 | timelineCount: 1 67 | timeline0: 0.0 68 | - Spawn Height - 69 | lowMin: 0.0 70 | lowMax: 0.0 71 | highMin: 0.0 72 | highMax: 0.0 73 | relative: false 74 | scalingCount: 1 75 | scaling0: 1.0 76 | timelineCount: 1 77 | timeline0: 0.0 78 | - X Scale - 79 | lowMin: 0.0 80 | lowMax: 0.0 81 | highMin: 200.0 82 | highMax: 200.0 83 | relative: false 84 | scalingCount: 1 85 | scaling0: 1.0 86 | timelineCount: 1 87 | timeline0: 0.0 88 | - Y Scale - 89 | active: false 90 | - Velocity - 91 | active: false 92 | - Angle - 93 | active: false 94 | - Rotation - 95 | active: false 96 | - Wind - 97 | active: false 98 | - Gravity - 99 | active: false 100 | - Tint - 101 | colorsCount: 27 102 | colors0: 1.0 103 | colors1: 0.047058824 104 | colors2: 0.047058824 105 | colors3: 1.0 106 | colors4: 0.69411767 107 | colors5: 0.047058824 108 | colors6: 0.95686275 109 | colors7: 1.0 110 | colors8: 0.047058824 111 | colors9: 0.047058824 112 | colors10: 1.0 113 | colors11: 0.09019608 114 | colors12: 0.047058824 115 | colors13: 0.9254902 116 | colors14: 1.0 117 | colors15: 0.047058824 118 | colors16: 0.11764706 119 | colors17: 1.0 120 | colors18: 0.4509804 121 | colors19: 0.047058824 122 | colors20: 1.0 123 | colors21: 1.0 124 | colors22: 0.047058824 125 | colors23: 0.972549 126 | colors24: 1.0 127 | colors25: 0.047058824 128 | colors26: 0.047058824 129 | timelineCount: 9 130 | timeline0: 0.0 131 | timeline1: 0.16351119 132 | timeline2: 0.2616179 133 | timeline3: 0.38554215 134 | timeline4: 0.51979345 135 | timeline5: 0.6506024 136 | timeline6: 0.7555938 137 | timeline7: 0.8915663 138 | timeline8: 1.0 139 | - Transparency - 140 | lowMin: 0.0 141 | lowMax: 0.0 142 | highMin: 1.0 143 | highMax: 1.0 144 | relative: false 145 | scalingCount: 1 146 | scaling0: 1.0 147 | timelineCount: 1 148 | timeline0: 0.0 149 | - Options - 150 | attached: true 151 | continuous: true 152 | aligned: false 153 | additive: true 154 | behind: false 155 | premultipliedAlpha: false 156 | spriteMode: single 157 | - Image Paths - 158 | logo.png 159 | 160 | -------------------------------------------------------------------------------- /assets/laser.p: -------------------------------------------------------------------------------- 1 | Laser 2 | - Delay - 3 | active: false 4 | - Duration - 5 | lowMin: 1000.0 6 | lowMax: 1000.0 7 | - Count - 8 | min: 0 9 | max: 200 10 | - Emission - 11 | lowMin: 0.0 12 | lowMax: 0.0 13 | highMin: 200.0 14 | highMax: 200.0 15 | relative: false 16 | scalingCount: 1 17 | scaling0: 1.0 18 | timelineCount: 1 19 | timeline0: 0.0 20 | - Life - 21 | lowMin: 0.0 22 | lowMax: 0.0 23 | highMin: 600.0 24 | highMax: 600.0 25 | relative: false 26 | scalingCount: 1 27 | scaling0: 1.0 28 | timelineCount: 1 29 | timeline0: 0.0 30 | independent: false 31 | - Life Offset - 32 | active: false 33 | independent: false 34 | - X Offset - 35 | active: false 36 | - Y Offset - 37 | active: false 38 | - Spawn Shape - 39 | shape: line 40 | - Spawn Width - 41 | lowMin: 0.0 42 | lowMax: 0.0 43 | highMin: 500.0 44 | highMax: 500.0 45 | relative: false 46 | scalingCount: 1 47 | scaling0: 1.0 48 | timelineCount: 1 49 | timeline0: 0.0 50 | - Spawn Height - 51 | lowMin: 0.0 52 | lowMax: 0.0 53 | highMin: 0.0 54 | highMax: 0.0 55 | relative: false 56 | scalingCount: 1 57 | scaling0: 1.0 58 | timelineCount: 1 59 | timeline0: 0.0 60 | - X Scale - 61 | lowMin: 0.0 62 | lowMax: 0.0 63 | highMin: 2.0 64 | highMax: 2.0 65 | relative: false 66 | scalingCount: 1 67 | scaling0: 1.0 68 | timelineCount: 1 69 | timeline0: 0.0 70 | - Y Scale - 71 | active: false 72 | - Velocity - 73 | active: true 74 | lowMin: 0.0 75 | lowMax: 90.0 76 | highMin: 0.0 77 | highMax: 90.0 78 | relative: false 79 | scalingCount: 2 80 | scaling0: 0.0 81 | scaling1: 1.0 82 | timelineCount: 2 83 | timeline0: 0.0 84 | timeline1: 1.0 85 | - Angle - 86 | active: true 87 | lowMin: 0.0 88 | lowMax: 0.0 89 | highMin: 0.0 90 | highMax: 0.0 91 | relative: false 92 | scalingCount: 1 93 | scaling0: 1.0 94 | timelineCount: 1 95 | timeline0: 0.0 96 | - Rotation - 97 | active: false 98 | - Wind - 99 | active: false 100 | - Gravity - 101 | active: false 102 | - Tint - 103 | colorsCount: 3 104 | colors0: 1.0 105 | colors1: 0.047058824 106 | colors2: 0.047058824 107 | timelineCount: 1 108 | timeline0: 0.0 109 | - Transparency - 110 | lowMin: 0.0 111 | lowMax: 0.0 112 | highMin: 1.0 113 | highMax: 1.0 114 | relative: false 115 | scalingCount: 3 116 | scaling0: 0.0 117 | scaling1: 1.0 118 | scaling2: 0.0 119 | timelineCount: 3 120 | timeline0: 0.0 121 | timeline1: 0.63013697 122 | timeline2: 1.0 123 | - Options - 124 | attached: true 125 | continuous: true 126 | aligned: true 127 | additive: false 128 | behind: false 129 | premultipliedAlpha: false 130 | spriteMode: single 131 | - Image Paths - 132 | particle.png 133 | 134 | -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libgdx/gdx-particle-editor/9b0aa9c8a2208c510a9d8a6b26c08f80b6aaf7dc/assets/logo.png -------------------------------------------------------------------------------- /assets/particle-ball1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libgdx/gdx-particle-editor/9b0aa9c8a2208c510a9d8a6b26c08f80b6aaf7dc/assets/particle-ball1.png -------------------------------------------------------------------------------- /assets/particle-ball2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libgdx/gdx-particle-editor/9b0aa9c8a2208c510a9d8a6b26c08f80b6aaf7dc/assets/particle-ball2.png -------------------------------------------------------------------------------- /assets/particle-ball3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libgdx/gdx-particle-editor/9b0aa9c8a2208c510a9d8a6b26c08f80b6aaf7dc/assets/particle-ball3.png -------------------------------------------------------------------------------- /assets/particle-ball4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libgdx/gdx-particle-editor/9b0aa9c8a2208c510a9d8a6b26c08f80b6aaf7dc/assets/particle-ball4.png -------------------------------------------------------------------------------- /assets/particle-cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libgdx/gdx-particle-editor/9b0aa9c8a2208c510a9d8a6b26c08f80b6aaf7dc/assets/particle-cloud.png -------------------------------------------------------------------------------- /assets/particle-fire.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libgdx/gdx-particle-editor/9b0aa9c8a2208c510a9d8a6b26c08f80b6aaf7dc/assets/particle-fire.png -------------------------------------------------------------------------------- /assets/particle-star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libgdx/gdx-particle-editor/9b0aa9c8a2208c510a9d8a6b26c08f80b6aaf7dc/assets/particle-star.png -------------------------------------------------------------------------------- /assets/particle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libgdx/gdx-particle-editor/9b0aa9c8a2208c510a9d8a6b26c08f80b6aaf7dc/assets/particle.png -------------------------------------------------------------------------------- /assets/pre_particle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libgdx/gdx-particle-editor/9b0aa9c8a2208c510a9d8a6b26c08f80b6aaf7dc/assets/pre_particle.png -------------------------------------------------------------------------------- /assets/rain.p: -------------------------------------------------------------------------------- 1 | Rain 2 | - Delay - 3 | active: false 4 | - Duration - 5 | lowMin: 1000.0 6 | lowMax: 1000.0 7 | - Count - 8 | min: 0 9 | max: 2000 10 | - Emission - 11 | lowMin: 0.0 12 | lowMax: 0.0 13 | highMin: 20.0 14 | highMax: 20.0 15 | relative: false 16 | scalingCount: 1 17 | scaling0: 1.0 18 | timelineCount: 1 19 | timeline0: 0.0 20 | - Life - 21 | lowMin: 0.0 22 | lowMax: 0.0 23 | highMin: 10000.0 24 | highMax: 10000.0 25 | relative: false 26 | scalingCount: 1 27 | scaling0: 1.0 28 | timelineCount: 1 29 | timeline0: 0.0 30 | independent: false 31 | - Life Offset - 32 | active: false 33 | independent: false 34 | - X Offset - 35 | active: false 36 | - Y Offset - 37 | active: false 38 | - Spawn Shape - 39 | shape: line 40 | - Spawn Width - 41 | lowMin: 0.0 42 | lowMax: 0.0 43 | highMin: 800.0 44 | highMax: 800.0 45 | relative: false 46 | scalingCount: 1 47 | scaling0: 1.0 48 | timelineCount: 1 49 | timeline0: 0.0 50 | - Spawn Height - 51 | lowMin: 0.0 52 | lowMax: 0.0 53 | highMin: 0.0 54 | highMax: 0.0 55 | relative: false 56 | scalingCount: 1 57 | scaling0: 1.0 58 | timelineCount: 1 59 | timeline0: 0.0 60 | - X Scale - 61 | lowMin: 0.0 62 | lowMax: 0.0 63 | highMin: 2.0 64 | highMax: 2.0 65 | relative: false 66 | scalingCount: 1 67 | scaling0: 1.0 68 | timelineCount: 1 69 | timeline0: 0.0 70 | - Y Scale - 71 | active: true 72 | lowMin: 0.0 73 | lowMax: 0.0 74 | highMin: 200.0 75 | highMax: 200.0 76 | relative: false 77 | scalingCount: 2 78 | scaling0: 0.0 79 | scaling1: 1.0 80 | timelineCount: 2 81 | timeline0: 0.0 82 | timeline1: 0.041666668 83 | - Velocity - 84 | active: true 85 | lowMin: 0.0 86 | lowMax: 0.0 87 | highMin: 520.0 88 | highMax: 600.0 89 | relative: false 90 | scalingCount: 1 91 | scaling0: 1.0 92 | timelineCount: 1 93 | timeline0: 0.0 94 | - Angle - 95 | active: true 96 | lowMin: 0.0 97 | lowMax: 0.0 98 | highMin: 240.0 99 | highMax: 240.0 100 | relative: false 101 | scalingCount: 1 102 | scaling0: 1.0 103 | timelineCount: 1 104 | timeline0: 0.0 105 | - Rotation - 106 | active: true 107 | lowMin: 0.0 108 | lowMax: 0.0 109 | highMin: 150.0 110 | highMax: 150.0 111 | relative: false 112 | scalingCount: 1 113 | scaling0: 1.0 114 | timelineCount: 1 115 | timeline0: 0.0 116 | - Wind - 117 | active: false 118 | - Gravity - 119 | active: false 120 | - Tint - 121 | colorsCount: 3 122 | colors0: 1.0 123 | colors1: 1.0 124 | colors2: 1.0 125 | timelineCount: 1 126 | timeline0: 0.0 127 | - Transparency - 128 | lowMin: 0.0 129 | lowMax: 0.0 130 | highMin: 1.0 131 | highMax: 1.0 132 | relative: false 133 | scalingCount: 4 134 | scaling0: 0.0 135 | scaling1: 1.0 136 | scaling2: 1.0 137 | scaling3: 0.0 138 | timelineCount: 4 139 | timeline0: 0.0 140 | timeline1: 0.11111132 141 | timeline2: 0.9027782 142 | timeline3: 1.0 143 | - Options - 144 | attached: false 145 | continuous: true 146 | aligned: false 147 | additive: false 148 | behind: false 149 | premultipliedAlpha: false 150 | spriteMode: single 151 | - Image Paths - 152 | particle.png 153 | 154 | -------------------------------------------------------------------------------- /assets/skin/gdxparticleeditor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libgdx/gdx-particle-editor/9b0aa9c8a2208c510a9d8a6b26c08f80b6aaf7dc/assets/skin/gdxparticleeditor.png -------------------------------------------------------------------------------- /assets/smoke.p: -------------------------------------------------------------------------------- 1 | Smoke 2 | - Delay - 3 | active: false 4 | - Duration - 5 | lowMin: 3000.0 6 | lowMax: 3000.0 7 | - Count - 8 | min: 0 9 | max: 200 10 | - Emission - 11 | lowMin: 0.0 12 | lowMax: 0.0 13 | highMin: 5.0 14 | highMax: 5.0 15 | relative: false 16 | scalingCount: 1 17 | scaling0: 1.0 18 | timelineCount: 1 19 | timeline0: 0.0 20 | - Life - 21 | lowMin: 0.0 22 | lowMax: 0.0 23 | highMin: 6000.0 24 | highMax: 6000.0 25 | relative: false 26 | scalingCount: 1 27 | scaling0: 1.0 28 | timelineCount: 1 29 | timeline0: 0.0 30 | independent: false 31 | - Life Offset - 32 | active: false 33 | independent: false 34 | - X Offset - 35 | active: false 36 | - Y Offset - 37 | active: false 38 | - Spawn Shape - 39 | shape: point 40 | - Spawn Width - 41 | lowMin: 0.0 42 | lowMax: 0.0 43 | highMin: 0.0 44 | highMax: 0.0 45 | relative: false 46 | scalingCount: 1 47 | scaling0: 1.0 48 | timelineCount: 1 49 | timeline0: 0.0 50 | - Spawn Height - 51 | lowMin: 0.0 52 | lowMax: 0.0 53 | highMin: 0.0 54 | highMax: 0.0 55 | relative: false 56 | scalingCount: 1 57 | scaling0: 1.0 58 | timelineCount: 1 59 | timeline0: 0.0 60 | - X Scale - 61 | lowMin: 40.0 62 | lowMax: 40.0 63 | highMin: 80.0 64 | highMax: 80.0 65 | relative: true 66 | scalingCount: 2 67 | scaling0: 0.0 68 | scaling1: 1.0 69 | timelineCount: 2 70 | timeline0: 0.0 71 | timeline1: 1.0 72 | - Y Scale - 73 | active: false 74 | - Velocity - 75 | active: true 76 | lowMin: 0.0 77 | lowMax: 0.0 78 | highMin: 50.0 79 | highMax: 50.0 80 | relative: false 81 | scalingCount: 1 82 | scaling0: 1.0 83 | timelineCount: 1 84 | timeline0: 0.0 85 | - Angle - 86 | active: true 87 | lowMin: 0.0 88 | lowMax: 0.0 89 | highMin: 75.0 90 | highMax: 105.0 91 | relative: false 92 | scalingCount: 1 93 | scaling0: 1.0 94 | timelineCount: 1 95 | timeline0: 0.0 96 | - Rotation - 97 | active: true 98 | lowMin: 0.0 99 | lowMax: 360.0 100 | highMin: -100.0 101 | highMax: 100.0 102 | relative: true 103 | scalingCount: 2 104 | scaling0: 0.0 105 | scaling1: 1.0 106 | timelineCount: 2 107 | timeline0: 0.0 108 | timeline1: 1.0 109 | - Wind - 110 | active: false 111 | - Gravity - 112 | active: false 113 | - Tint - 114 | colorsCount: 3 115 | colors0: 0.5764706 116 | colors1: 0.5764706 117 | colors2: 0.5764706 118 | timelineCount: 1 119 | timeline0: 0.0 120 | - Transparency - 121 | lowMin: 0.0 122 | lowMax: 0.0 123 | highMin: 1.0 124 | highMax: 1.0 125 | relative: false 126 | scalingCount: 4 127 | scaling0: 0.0 128 | scaling1: 1.0 129 | scaling2: 1.0 130 | scaling3: 0.0 131 | timelineCount: 4 132 | timeline0: 0.0 133 | timeline1: 0.21527798 134 | timeline2: 0.805556 135 | timeline3: 1.0 136 | - Options - 137 | attached: false 138 | continuous: true 139 | aligned: false 140 | additive: false 141 | behind: false 142 | premultipliedAlpha: false 143 | spriteMode: single 144 | - Image Paths - 145 | particle-cloud.png 146 | 147 | -------------------------------------------------------------------------------- /assets/sparks.p: -------------------------------------------------------------------------------- 1 | Sparks 2 | - Delay - 3 | active: false 4 | - Duration - 5 | lowMin: 3000.0 6 | lowMax: 3000.0 7 | - Count - 8 | min: 0 9 | max: 200 10 | - Emission - 11 | lowMin: 0.0 12 | lowMax: 0.0 13 | highMin: 90.0 14 | highMax: 90.0 15 | relative: false 16 | scalingCount: 1 17 | scaling0: 1.0 18 | timelineCount: 1 19 | timeline0: 0.0 20 | - Life - 21 | lowMin: 0.0 22 | lowMax: 0.0 23 | highMin: 500.0 24 | highMax: 1000.0 25 | relative: false 26 | scalingCount: 1 27 | scaling0: 1.0 28 | timelineCount: 1 29 | timeline0: 0.0 30 | independent: false 31 | - Life Offset - 32 | active: false 33 | independent: false 34 | - X Offset - 35 | active: false 36 | - Y Offset - 37 | active: false 38 | - Spawn Shape - 39 | shape: point 40 | - Spawn Width - 41 | lowMin: 0.0 42 | lowMax: 0.0 43 | highMin: 0.0 44 | highMax: 0.0 45 | relative: false 46 | scalingCount: 1 47 | scaling0: 1.0 48 | timelineCount: 1 49 | timeline0: 0.0 50 | - Spawn Height - 51 | lowMin: 0.0 52 | lowMax: 0.0 53 | highMin: 0.0 54 | highMax: 0.0 55 | relative: false 56 | scalingCount: 1 57 | scaling0: 1.0 58 | timelineCount: 1 59 | timeline0: 0.0 60 | - X Scale - 61 | lowMin: 0.0 62 | lowMax: 0.0 63 | highMin: 5.0 64 | highMax: 19.0 65 | relative: false 66 | scalingCount: 3 67 | scaling0: 0.0 68 | scaling1: 1.0 69 | scaling2: 0.0 70 | timelineCount: 3 71 | timeline0: 0.0 72 | timeline1: 0.30555555 73 | timeline2: 1.0 74 | - Y Scale - 75 | active: false 76 | - Velocity - 77 | active: true 78 | lowMin: 0.0 79 | lowMax: 0.0 80 | highMin: 30.0 81 | highMax: 285.0 82 | relative: false 83 | scalingCount: 1 84 | scaling0: 1.0 85 | timelineCount: 1 86 | timeline0: 0.0 87 | - Angle - 88 | active: true 89 | lowMin: 0.0 90 | lowMax: 0.0 91 | highMin: 0.0 92 | highMax: 360.0 93 | relative: false 94 | scalingCount: 1 95 | scaling0: 1.0 96 | timelineCount: 1 97 | timeline0: 0.0 98 | - Rotation - 99 | active: true 100 | lowMin: 0.0 101 | lowMax: 360.0 102 | highMin: -360.0 103 | highMax: 360.0 104 | relative: true 105 | scalingCount: 2 106 | scaling0: 0.0 107 | scaling1: 1.0 108 | timelineCount: 2 109 | timeline0: 0.0 110 | timeline1: 1.0 111 | - Wind - 112 | active: false 113 | - Gravity - 114 | active: true 115 | lowMin: 0.0 116 | lowMax: 0.0 117 | highMin: -405.0 118 | highMax: -405.0 119 | relative: true 120 | scalingCount: 2 121 | scaling0: 0.0 122 | scaling1: 1.0 123 | timelineCount: 2 124 | timeline0: 0.0 125 | timeline1: 1.0 126 | - Tint - 127 | colorsCount: 3 128 | colors0: 0.93333334 129 | colors1: 1.0 130 | colors2: 0.3764706 131 | timelineCount: 1 132 | timeline0: 0.0 133 | - Transparency - 134 | lowMin: 0.0 135 | lowMax: 0.0 136 | highMin: 1.0 137 | highMax: 1.0 138 | relative: false 139 | scalingCount: 4 140 | scaling0: 0.0 141 | scaling1: 1.0 142 | scaling2: 0.75 143 | scaling3: 0.0 144 | timelineCount: 4 145 | timeline0: 0.0 146 | timeline1: 0.2 147 | timeline2: 0.8 148 | timeline3: 1.0 149 | - Options - 150 | attached: false 151 | continuous: true 152 | aligned: false 153 | additive: false 154 | behind: false 155 | premultipliedAlpha: false 156 | spriteMode: single 157 | - Image Paths - 158 | particle-star.png 159 | 160 | -------------------------------------------------------------------------------- /assets/thruster.p: -------------------------------------------------------------------------------- 1 | Thruster 2 | - Delay - 3 | active: false 4 | - Duration - 5 | lowMin: 200.0 6 | lowMax: 200.0 7 | - Count - 8 | min: 0 9 | max: 2000 10 | - Emission - 11 | lowMin: 0.0 12 | lowMax: 0.0 13 | highMin: 75.0 14 | highMax: 75.0 15 | relative: false 16 | scalingCount: 1 17 | scaling0: 1.0 18 | timelineCount: 1 19 | timeline0: 0.0 20 | - Life - 21 | lowMin: 0.0 22 | lowMax: 0.0 23 | highMin: 500.0 24 | highMax: 500.0 25 | relative: false 26 | scalingCount: 1 27 | scaling0: 1.0 28 | timelineCount: 1 29 | timeline0: 0.0 30 | independent: false 31 | - Life Offset - 32 | active: false 33 | independent: false 34 | - X Offset - 35 | active: false 36 | - Y Offset - 37 | active: false 38 | - Spawn Shape - 39 | shape: point 40 | - Spawn Width - 41 | lowMin: 0.0 42 | lowMax: 0.0 43 | highMin: 0.0 44 | highMax: 0.0 45 | relative: false 46 | scalingCount: 1 47 | scaling0: 1.0 48 | timelineCount: 1 49 | timeline0: 0.0 50 | - Spawn Height - 51 | lowMin: 0.0 52 | lowMax: 0.0 53 | highMin: 0.0 54 | highMax: 0.0 55 | relative: false 56 | scalingCount: 1 57 | scaling0: 1.0 58 | timelineCount: 1 59 | timeline0: 0.0 60 | - X Scale - 61 | lowMin: 0.0 62 | lowMax: 0.0 63 | highMin: 30.0 64 | highMax: 30.0 65 | relative: false 66 | scalingCount: 2 67 | scaling0: 1.0 68 | scaling1: 0.0 69 | timelineCount: 2 70 | timeline0: 0.0 71 | timeline1: 1.0 72 | - Y Scale - 73 | active: false 74 | - Velocity - 75 | active: true 76 | lowMin: 0.0 77 | lowMax: 0.0 78 | highMin: 110.0 79 | highMax: 110.0 80 | relative: false 81 | scalingCount: 1 82 | scaling0: 1.0 83 | timelineCount: 1 84 | timeline0: 0.0 85 | - Angle - 86 | active: true 87 | lowMin: 0.0 88 | lowMax: 0.0 89 | highMin: 265.0 90 | highMax: 275.0 91 | relative: false 92 | scalingCount: 1 93 | scaling0: 1.0 94 | timelineCount: 1 95 | timeline0: 0.0 96 | - Rotation - 97 | active: true 98 | lowMin: 0.0 99 | lowMax: 0.0 100 | highMin: 0.0 101 | highMax: 360.0 102 | relative: false 103 | scalingCount: 1 104 | scaling0: 1.0 105 | timelineCount: 1 106 | timeline0: 0.0 107 | - Wind - 108 | active: false 109 | - Gravity - 110 | active: false 111 | - Tint - 112 | colorsCount: 12 113 | colors0: 1.0 114 | colors1: 1.0 115 | colors2: 1.0 116 | colors3: 0.96862745 117 | colors4: 0.9137255 118 | colors5: 0.0 119 | colors6: 0.96862745 120 | colors7: 0.4627451 121 | colors8: 0.0 122 | colors9: 0.96862745 123 | colors10: 0.0 124 | colors11: 0.0 125 | timelineCount: 4 126 | timeline0: 0.0 127 | timeline1: 0.13018873 128 | timeline2: 0.34528303 129 | timeline3: 1.0 130 | - Transparency - 131 | lowMin: 0.0 132 | lowMax: 0.0 133 | highMin: 1.0 134 | highMax: 1.0 135 | relative: false 136 | scalingCount: 4 137 | scaling0: 0.0 138 | scaling1: 1.0 139 | scaling2: 1.0 140 | scaling3: 0.0 141 | timelineCount: 4 142 | timeline0: 0.0 143 | timeline1: 0.11111132 144 | timeline2: 0.7986111 145 | timeline3: 1.0 146 | - Options - 147 | attached: true 148 | continuous: true 149 | aligned: false 150 | additive: true 151 | behind: false 152 | premultipliedAlpha: false 153 | spriteMode: single 154 | - Image Paths - 155 | particle-fire.png 156 | 157 | -------------------------------------------------------------------------------- /assets/trail.p: -------------------------------------------------------------------------------- 1 | Trail 2 | - Delay - 3 | active: false 4 | - Duration - 5 | lowMin: 200.0 6 | lowMax: 200.0 7 | - Count - 8 | min: 0 9 | max: 2000 10 | - Emission - 11 | lowMin: 0.0 12 | lowMax: 0.0 13 | highMin: 20.0 14 | highMax: 20.0 15 | relative: false 16 | scalingCount: 1 17 | scaling0: 1.0 18 | timelineCount: 1 19 | timeline0: 0.0 20 | - Life - 21 | lowMin: 0.0 22 | lowMax: 0.0 23 | highMin: 500.0 24 | highMax: 500.0 25 | relative: false 26 | scalingCount: 1 27 | scaling0: 1.0 28 | timelineCount: 1 29 | timeline0: 0.0 30 | independent: false 31 | - Life Offset - 32 | active: false 33 | independent: false 34 | - X Offset - 35 | active: false 36 | - Y Offset - 37 | active: false 38 | - Spawn Shape - 39 | shape: point 40 | - Spawn Width - 41 | lowMin: 0.0 42 | lowMax: 0.0 43 | highMin: 0.0 44 | highMax: 0.0 45 | relative: false 46 | scalingCount: 1 47 | scaling0: 1.0 48 | timelineCount: 1 49 | timeline0: 0.0 50 | - Spawn Height - 51 | lowMin: 0.0 52 | lowMax: 0.0 53 | highMin: 0.0 54 | highMax: 0.0 55 | relative: false 56 | scalingCount: 1 57 | scaling0: 1.0 58 | timelineCount: 1 59 | timeline0: 0.0 60 | - X Scale - 61 | lowMin: 0.0 62 | lowMax: 0.0 63 | highMin: 5.0 64 | highMax: 5.0 65 | relative: false 66 | scalingCount: 2 67 | scaling0: 1.0 68 | scaling1: 0.0 69 | timelineCount: 2 70 | timeline0: 0.0 71 | timeline1: 1.0 72 | - Y Scale - 73 | active: false 74 | - Velocity - 75 | active: false 76 | - Angle - 77 | active: false 78 | - Rotation - 79 | active: false 80 | - Wind - 81 | active: false 82 | - Gravity - 83 | active: false 84 | - Tint - 85 | colorsCount: 3 86 | colors0: 1.0 87 | colors1: 1.0 88 | colors2: 1.0 89 | timelineCount: 1 90 | timeline0: 0.0 91 | - Transparency - 92 | lowMin: 0.0 93 | lowMax: 0.0 94 | highMin: 1.0 95 | highMax: 1.0 96 | relative: false 97 | scalingCount: 1 98 | scaling0: 1.0 99 | timelineCount: 1 100 | timeline0: 0.0 101 | - Options - 102 | attached: false 103 | continuous: true 104 | aligned: false 105 | additive: false 106 | behind: false 107 | premultipliedAlpha: false 108 | spriteMode: single 109 | - Image Paths - 110 | particle.png 111 | 112 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | mavenCentral() 4 | maven { url 'https://s01.oss.sonatype.org' } 5 | mavenLocal() 6 | google() 7 | gradlePluginPortal() 8 | maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' } 9 | maven { url 'https://s01.oss.sonatype.org/content/repositories/snapshots/' } 10 | } 11 | dependencies { 12 | classpath "io.freefair.gradle:lombok-plugin:6.5.0.3" 13 | 14 | } 15 | } 16 | 17 | allprojects { 18 | apply plugin: 'eclipse' 19 | apply plugin: 'idea' 20 | } 21 | 22 | configure(subprojects) { 23 | apply plugin: 'java-library' 24 | apply plugin: 'io.freefair.lombok' 25 | sourceCompatibility = 11 26 | compileJava { 27 | options.incremental = true 28 | } 29 | } 30 | 31 | task versionText { 32 | doLast { 33 | new File(projectDir, "core/src/main/resources/version").text = project(':core').version 34 | } 35 | } 36 | 37 | subprojects { 38 | version = '1.0.9' 39 | ext.appName = 'gdx-particle-editor' 40 | repositories { 41 | mavenCentral() 42 | maven { url 'https://s01.oss.sonatype.org' } 43 | // You may want to remove the following line if you have errors downloading dependencies. 44 | mavenLocal() 45 | gradlePluginPortal() 46 | maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' } 47 | maven { url 'https://s01.oss.sonatype.org/content/repositories/snapshots/' } 48 | maven { url 'https://jitpack.io' } 49 | } 50 | } 51 | 52 | eclipse.project.name = 'gdx-particle-editor' + '-parent' 53 | -------------------------------------------------------------------------------- /core/build.gradle: -------------------------------------------------------------------------------- 1 | [compileJava, compileTestJava]*.options*.encoding = 'UTF-8' 2 | eclipse.project.name = appName + '-core' 3 | 4 | buildscript { 5 | repositories { 6 | gradlePluginPortal() 7 | } 8 | dependencies { 9 | // using jpackage only works if the JDK version is 14 or higher. 10 | // your JAVA_HOME environment variable may also need to be a JDK with version 14 or higher. 11 | if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_14)) { 12 | classpath "org.beryx:badass-runtime-plugin:1.13.0" 13 | } 14 | } 15 | } 16 | 17 | if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_14)) { 18 | apply plugin: 'org.beryx.runtime' 19 | } 20 | else { 21 | apply plugin: 'application' 22 | } 23 | 24 | sourceSets.main.resources.srcDirs += [ rootProject.file('assets').path ] 25 | mainClassName = 'com.ray3k.gdxparticleeditor.lwjgl3.Lwjgl3Launcher' 26 | sourceCompatibility = 11 27 | 28 | dependencies { 29 | api "com.badlogicgames.gdx:gdx:$gdxVersion" 30 | implementation "com.badlogicgames.gdx:gdx-backend-lwjgl3:$gdxVersion" 31 | implementation "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop" 32 | api "org.projectlombok:lombok:$lombokVersion" 33 | annotationProcessor "org.projectlombok:lombok:$lombokVersion" 34 | api 'com.github.raeleus.TenPatch:tenpatch:5.2.3' 35 | implementation 'com.github.raeleus.stripe:stripe:1.4.5' 36 | implementation 'com.github.raeleus.stripe:colorpicker:1.4.5' 37 | implementation 'space.earlygrey:shapedrawer:2.5.0' 38 | implementation 'com.github.tommyettinger:colorful:0.8.4' 39 | implementation "org.lwjgl:lwjgl-tinyfd:3.3.2" 40 | implementation "org.lwjgl:lwjgl-tinyfd:3.3.2:natives-windows" 41 | implementation "org.lwjgl:lwjgl-tinyfd:3.3.2:natives-linux" 42 | implementation "org.lwjgl:lwjgl-tinyfd:3.3.2:natives-macos" 43 | implementation "org.lwjgl:lwjgl-tinyfd:3.3.2:natives-macos-arm64" 44 | } 45 | 46 | def os = System.properties['os.name'].toLowerCase() 47 | 48 | run { 49 | workingDir = rootProject.file('assets').path 50 | setIgnoreExitValue(true) 51 | 52 | if (os.contains('mac')) { 53 | // Required to run LWJGL3 Java apps on macOS 54 | jvmArgs += "-XstartOnFirstThread" 55 | } 56 | } 57 | 58 | jar { 59 | // sets the name of the .jar file this produces to the name of the game or app. 60 | archiveFileName.set(appName + ".jar") 61 | // using 'lib' instead of the default 'libs' appears to be needed by jpackageimage. 62 | destinationDirectory = file("$project.buildDir/lib") 63 | // the duplicatesStrategy matters starting in Gradle 7.0; this setting works. 64 | duplicatesStrategy(DuplicatesStrategy.EXCLUDE) 65 | dependsOn configurations.runtimeClasspath 66 | from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } 67 | // these "exclude" lines remove some unnecessary duplicate files in the output JAR. 68 | exclude('META-INF/INDEX.LIST', 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA') 69 | dependencies { 70 | exclude('META-INF/INDEX.LIST', 'META-INF/maven/**') 71 | } 72 | // setting the manifest makes the JAR runnable. 73 | manifest { 74 | attributes 'Main-Class': project.mainClassName 75 | } 76 | // this last step may help on some OSes that need extra instruction to make runnable JARs. 77 | doLast { 78 | file(archiveFile).setExecutable(true, false) 79 | } 80 | } 81 | 82 | if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_14)) { 83 | tasks.jpackageImage.doNotTrackState("This task both reads from and writes to the build folder.") 84 | runtime { 85 | options.set(['--strip-debug', 86 | '--compress', '2', 87 | '--no-header-files', 88 | '--no-man-pages', 89 | '--strip-native-commands', 90 | '--vm', 'server']) 91 | // you could very easily need more modules than this one. 92 | // use the lwjgl3:suggestModules task to see which modules may be needed. 93 | modules.set([ 94 | 'jdk.unsupported' 95 | ]) 96 | distDir.set(file(buildDir)) 97 | jpackage { 98 | imageName = appName 99 | // you can set this to false if you want to build an installer, or keep it as true to build just an app. 100 | skipInstaller = true 101 | // this may need to be set to a different path if your JAVA_HOME points to a low JDK version. 102 | jpackageHome = javaHome.getOrElse("") 103 | mainJar = jar.archiveFileName.get() 104 | if (os.contains('win')) { 105 | imageOptions = ["--icon", "icons/logo.ico"] 106 | } else if (os.contains('nix') || os.contains('nux') || os.contains('bsd')) { 107 | imageOptions = ["--icon", "icons/logo.png"] 108 | } else if (os.contains('mac')) { 109 | imageOptions = ["--icon", "icons/logo.icns", "--java-options", "\"-XstartOnFirstThread\""] 110 | } 111 | } 112 | } 113 | } 114 | 115 | // Equivalent to the jar task; here for compatibility with gdx-setup. 116 | tasks.register('dist') { 117 | dependsOn['jar'] 118 | } 119 | -------------------------------------------------------------------------------- /core/icons/logo.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libgdx/gdx-particle-editor/9b0aa9c8a2208c510a9d8a6b26c08f80b6aaf7dc/core/icons/logo.icns -------------------------------------------------------------------------------- /core/icons/logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libgdx/gdx-particle-editor/9b0aa9c8a2208c510a9d8a6b26c08f80b6aaf7dc/core/icons/logo.ico -------------------------------------------------------------------------------- /core/icons/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libgdx/gdx-particle-editor/9b0aa9c8a2208c510a9d8a6b26c08f80b6aaf7dc/core/icons/logo.png -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/FileDialogs.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor; 2 | 3 | import com.badlogic.gdx.Gdx; 4 | import com.badlogic.gdx.files.FileHandle; 5 | import com.badlogic.gdx.scenes.scene2d.utils.UIUtils; 6 | import com.badlogic.gdx.utils.Array; 7 | import org.lwjgl.PointerBuffer; 8 | import org.lwjgl.system.MemoryStack; 9 | import org.lwjgl.util.tinyfd.TinyFileDialogs; 10 | 11 | /** 12 | * A convenience class that provides static methods to open OS native file dialogs. 13 | */ 14 | public class FileDialogs { 15 | public static Array openMultipleDialog(String title, String defaultPath, String[] filterPatterns, String filterDescription) { 16 | //fix file path characters 17 | if (UIUtils.isWindows) { 18 | defaultPath = defaultPath.replace("/", "\\"); 19 | if (!defaultPath.endsWith("\\")) defaultPath += "\\"; 20 | } else { 21 | defaultPath = defaultPath.replace("\\", "/"); 22 | if (!defaultPath.endsWith("/")) defaultPath += "/"; 23 | } 24 | 25 | MemoryStack stack = MemoryStack.stackPush(); 26 | 27 | PointerBuffer filtersPointerBuffer = null; 28 | if (filterPatterns != null) { 29 | filtersPointerBuffer = stack.mallocPointer(filterPatterns.length); 30 | 31 | for (int i = 0; i < filterPatterns.length; i++) { 32 | filtersPointerBuffer.put(stack.UTF8("*." + filterPatterns[i])); 33 | } 34 | filtersPointerBuffer.flip(); 35 | } 36 | 37 | var response = TinyFileDialogs.tinyfd_openFileDialog(title, defaultPath, filtersPointerBuffer, filterDescription, true); 38 | 39 | if (response == null) return null; 40 | 41 | var strings = response.split("\\|"); 42 | var returnValue = new Array(); 43 | for (var string : strings) { 44 | var file = Gdx.files.absolute(string); 45 | if (!file.exists()) continue; 46 | returnValue.add(file); 47 | } 48 | 49 | if (returnValue.size == 0) return null; 50 | 51 | return returnValue; 52 | } 53 | 54 | public static FileHandle openDialog(String title, String defaultPath, String[] filterPatterns, String filterDescription) { 55 | //fix file path characters 56 | if (UIUtils.isWindows) { 57 | defaultPath = defaultPath.replace("/", "\\"); 58 | if (!defaultPath.endsWith("\\")) defaultPath += "\\"; 59 | } else { 60 | defaultPath = defaultPath.replace("\\", "/"); 61 | if (!defaultPath.endsWith("/")) defaultPath += "/"; 62 | } 63 | 64 | MemoryStack stack = MemoryStack.stackPush(); 65 | 66 | PointerBuffer filtersPointerBuffer = null; 67 | if (filterPatterns != null) { 68 | filtersPointerBuffer = stack.mallocPointer(filterPatterns.length); 69 | 70 | for (int i = 0; i < filterPatterns.length; i++) { 71 | filtersPointerBuffer.put(stack.UTF8("*." + filterPatterns[i])); 72 | } 73 | filtersPointerBuffer.flip(); 74 | } 75 | 76 | var response = TinyFileDialogs.tinyfd_openFileDialog(title, defaultPath, filtersPointerBuffer, filterDescription, true); 77 | 78 | if (response == null) return null; 79 | 80 | var file = Gdx.files.absolute(response); 81 | 82 | if (!file.exists()) return null; 83 | 84 | return file; 85 | } 86 | 87 | public static FileHandle saveDialog(String title, String defaultPath, String defaultName, String[] filterPatterns, String filterDescription) { 88 | //fix file path characters 89 | if (UIUtils.isWindows) { 90 | defaultPath = defaultPath.replace("/", "\\"); 91 | if (!defaultPath.endsWith("\\")) defaultPath += "\\"; 92 | } else { 93 | defaultPath = defaultPath.replace("\\", "/"); 94 | if (!defaultPath.endsWith("/")) defaultPath += "/"; 95 | } 96 | 97 | MemoryStack stack = MemoryStack.stackPush(); 98 | 99 | PointerBuffer filtersPointerBuffer = null; 100 | if (filterPatterns != null) { 101 | filtersPointerBuffer = stack.mallocPointer(filterPatterns.length); 102 | 103 | for (int i = 0; i < filterPatterns.length; i++) { 104 | filtersPointerBuffer.put(stack.UTF8("*." + filterPatterns[i])); 105 | } 106 | filtersPointerBuffer.flip(); 107 | } 108 | 109 | var response = TinyFileDialogs.tinyfd_saveFileDialog(title, defaultPath, filtersPointerBuffer, filterDescription); 110 | 111 | if (response == null) return null; 112 | 113 | return Gdx.files.absolute(response); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/ParticlePreview.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor; 2 | 3 | import com.badlogic.gdx.Gdx; 4 | import com.badlogic.gdx.graphics.Color; 5 | import com.badlogic.gdx.math.Vector2; 6 | import com.badlogic.gdx.scenes.scene2d.utils.UIUtils; 7 | import com.badlogic.gdx.utils.ScreenUtils; 8 | 9 | import static com.ray3k.gdxparticleeditor.Core.*; 10 | import static com.ray3k.gdxparticleeditor.PreviewSettings.*; 11 | 12 | /** 13 | * This class renders the particle preview. 14 | */ 15 | public class ParticlePreview { 16 | private static final Vector2 temp = new Vector2(); 17 | public static boolean pause; 18 | 19 | public void render() { 20 | var isDrawing = spriteBatch.isDrawing(); 21 | if (!isDrawing) { 22 | spriteBatch.setProjectionMatrix(previewViewport.getCamera().combined); 23 | spriteBatch.begin(); 24 | } 25 | spriteBatch.setColor(Color.WHITE); 26 | 27 | //calculate world coordinates 28 | temp.set(previewViewport.getScreenX(), Gdx.graphics.getHeight() - previewViewport.getScreenY()); 29 | previewViewport.unproject(temp); 30 | float left = temp.x; 31 | float bottom = temp.y; 32 | 33 | temp.set(previewViewport.getScreenX() + previewViewport.getScreenWidth(), Gdx.graphics.getHeight() - (previewViewport.getScreenY() + previewViewport.getScreenHeight())); 34 | previewViewport.unproject(temp); 35 | float right = temp.x; 36 | float top = temp.y; 37 | 38 | //draw background color 39 | shapeDrawer.setColor(getBackgroundColor()); 40 | shapeDrawer.filledRectangle(left, bottom, right - left, top - bottom); 41 | 42 | //draw preview 43 | if (previewImageTexture != null) spriteBatch.draw(previewImageTexture, previewImageX, previewImageY, previewImageWidth, previewImageHeight); 44 | 45 | if (isGridEnabled()) { 46 | //draw major grid 47 | shapeDrawer.setColor(getGridColor()); 48 | shapeDrawer.setDefaultLineWidth(2 * previewViewport.getUnitsPerPixel()); 49 | for (float x = left - left % getGridMajorGridlines(); x < right; x += getGridMajorGridlines()) { 50 | shapeDrawer.line(x, bottom, x, top); 51 | } 52 | for (float y = bottom - bottom % getGridMajorGridlines(); y < top; y += getGridMajorGridlines()) { 53 | shapeDrawer.line(left, y, right, y); 54 | } 55 | 56 | //draw minor grid 57 | shapeDrawer.setColor(getGridColor()); 58 | shapeDrawer.setDefaultLineWidth(1 * previewViewport.getUnitsPerPixel()); 59 | for (float x = left - left % getGridMinorGridlines(); x < right; x += getGridMinorGridlines()) { 60 | shapeDrawer.line(x, bottom, x, top); 61 | } 62 | for (float y = bottom - bottom % getGridMinorGridlines(); y < top; y += getGridMinorGridlines()) { 63 | shapeDrawer.line(left, y, right, y); 64 | } 65 | } 66 | 67 | if (isAxesEnabled()) { 68 | //draw axes 69 | shapeDrawer.setColor(getAxesColor()); 70 | shapeDrawer.setDefaultLineWidth(3 * previewViewport.getUnitsPerPixel()); 71 | if (bottom <= 0) shapeDrawer.line(left, 0, right, 0); 72 | if (left <= 0) shapeDrawer.line(0, bottom, 0, top); 73 | } 74 | 75 | if (!pause) { 76 | particleEffect.update(Gdx.graphics.getDeltaTime() * getDeltaMultiplier()); 77 | particleEffect.draw(spriteBatch); 78 | } 79 | 80 | if (!isDrawing) spriteBatch.end(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/SplitPaneSystemCursorListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2022 Raymond Buckley. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package com.ray3k.gdxparticleeditor; 25 | 26 | import com.badlogic.gdx.Gdx; 27 | import com.badlogic.gdx.graphics.Cursor.SystemCursor; 28 | import com.badlogic.gdx.scenes.scene2d.Actor; 29 | import com.badlogic.gdx.scenes.scene2d.InputEvent; 30 | import com.badlogic.gdx.scenes.scene2d.utils.DragListener; 31 | 32 | /** 33 | * A listener that changes the system cursor when the user enters the actor. The cursor does not change while the user 34 | * is dragging on the actor. 35 | * @author Raymond 36 | */ 37 | public class SplitPaneSystemCursorListener extends DragListener { 38 | private boolean draggingCursor; 39 | private SystemCursor systemCursor; 40 | 41 | public SplitPaneSystemCursorListener(SystemCursor systemCursor) { 42 | this.draggingCursor = false; 43 | this.systemCursor = systemCursor; 44 | } 45 | 46 | @Override 47 | public void exit(InputEvent event, float x, float y, int pointer, Actor toActor) { 48 | if (pointer == -1 && !draggingCursor && event.getListenerActor().equals(event.getTarget())) { 49 | Gdx.graphics.setSystemCursor(SystemCursor.Arrow); 50 | } 51 | } 52 | 53 | @Override 54 | public void enter(InputEvent event, float x, float y, int pointer, Actor fromActor) { 55 | if (!draggingCursor && event.getListenerActor().equals(event.getTarget())) { 56 | Gdx.graphics.setSystemCursor(systemCursor); 57 | } 58 | } 59 | 60 | @Override 61 | public void dragStop(InputEvent event, float x, float y, int pointer) { 62 | if (draggingCursor && !event.getListenerActor().equals(event.getTarget())) { 63 | Gdx.graphics.setSystemCursor(SystemCursor.Arrow); 64 | } 65 | draggingCursor = false; 66 | } 67 | 68 | @Override 69 | public void dragStart(InputEvent event, float x, float y, 70 | int pointer) { 71 | if (!draggingCursor && event.getListenerActor().equals(event.getTarget())) { 72 | draggingCursor = true; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/SystemCursorListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2022 Raymond Buckley. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package com.ray3k.gdxparticleeditor; 25 | 26 | import com.badlogic.gdx.Gdx; 27 | import com.badlogic.gdx.graphics.Cursor; 28 | import com.badlogic.gdx.graphics.Cursor.SystemCursor; 29 | import com.badlogic.gdx.scenes.scene2d.Actor; 30 | import com.badlogic.gdx.scenes.scene2d.InputEvent; 31 | import com.badlogic.gdx.scenes.scene2d.utils.Disableable; 32 | import com.badlogic.gdx.scenes.scene2d.utils.DragListener; 33 | 34 | /** 35 | * A listener that changes the system cursor when the user enters the actor. 36 | * @author Raymond 37 | */ 38 | public class SystemCursorListener extends DragListener { 39 | public boolean ignoreDisabled; 40 | private boolean draggingCursor; 41 | private SystemCursor systemCursor; 42 | 43 | public SystemCursorListener(SystemCursor systemCursor) { 44 | this.draggingCursor = false; 45 | this.systemCursor = systemCursor; 46 | } 47 | 48 | @Override 49 | public void exit(InputEvent event, float x, float y, int pointer, Actor toActor) { 50 | if (pointer == -1 && !draggingCursor) { 51 | Gdx.graphics.setSystemCursor(Cursor.SystemCursor.Arrow); 52 | } 53 | } 54 | 55 | @Override 56 | public void enter(InputEvent event, float x, float y, int pointer, Actor fromActor) { 57 | if (!draggingCursor && ignoreDisabled || !isDisabled(event.getListenerActor())) { 58 | Gdx.graphics.setSystemCursor(systemCursor); 59 | } 60 | } 61 | 62 | @Override 63 | public void dragStop(InputEvent event, float x, float y, int pointer) { 64 | if (draggingCursor) { 65 | Gdx.graphics.setSystemCursor(Cursor.SystemCursor.Arrow); 66 | } 67 | draggingCursor = false; 68 | } 69 | 70 | @Override 71 | public void dragStart(InputEvent event, float x, float y, int pointer) { 72 | if (!draggingCursor) { 73 | draggingCursor = true; 74 | } 75 | } 76 | 77 | private boolean isDisabled(Actor actor) { 78 | return actor instanceof Disableable && ((Disableable) actor).isDisabled(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/TextFileApplicationLogger.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2022 Raymond Buckley 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | ******************************************************************************/ 24 | 25 | package com.ray3k.gdxparticleeditor; 26 | 27 | import com.badlogic.gdx.ApplicationLogger; 28 | import com.badlogic.gdx.files.FileHandle; 29 | import com.badlogic.gdx.scenes.scene2d.utils.UIUtils; 30 | 31 | import java.io.PrintStream; 32 | import java.sql.Timestamp; 33 | import java.util.Date; 34 | 35 | /** 36 | * A logger that saves errors and messages to the provided logFilehandle. 37 | */ 38 | public class TextFileApplicationLogger implements ApplicationLogger { 39 | private final FileHandle logFileHandle; 40 | 41 | public TextFileApplicationLogger(FileHandle logFileHandle) { 42 | this.logFileHandle = logFileHandle; 43 | } 44 | 45 | @Override 46 | public void log(String tag, String message) { 47 | logFileHandle.writeString("\n" + new Timestamp(System.currentTimeMillis()) + "\n" + retrieveSystemDetails() + tag + ": " + message + "\n", true); 48 | 49 | System.out.println(tag + ": " + message); 50 | } 51 | 52 | @Override 53 | public void log(String tag, String message, Throwable exception) { 54 | logFileHandle.writeString("\n" + new Timestamp(System.currentTimeMillis()) + "\n" + retrieveSystemDetails() + tag + ": " + message + "\n", true); 55 | printException(exception); 56 | 57 | System.out.println(tag + ": " + message); 58 | exception.printStackTrace(System.out); 59 | } 60 | 61 | @Override 62 | public void error(String tag, String message) { 63 | logFileHandle.writeString("\n" + new Timestamp(System.currentTimeMillis()) + "\n" + retrieveSystemDetails() + tag + ": " + message + "\n", true); 64 | 65 | System.err.println(tag + ": " + message); 66 | } 67 | 68 | @Override 69 | public void error(String tag, String message, Throwable exception) { 70 | logFileHandle.writeString("\n" + new Timestamp(System.currentTimeMillis()) + "\n" + retrieveSystemDetails() + tag + ": " + message + "\n", true); 71 | printException(exception); 72 | 73 | System.err.println(tag + ": " + message); 74 | exception.printStackTrace(System.err); 75 | } 76 | 77 | @Override 78 | public void debug(String tag, String message) { 79 | logFileHandle.writeString("\n" + new Timestamp(System.currentTimeMillis()) + "\n" + retrieveSystemDetails() + tag + ": " + message + "\n", true); 80 | 81 | System.out.println(tag + ": " + message); 82 | } 83 | 84 | @Override 85 | public void debug(String tag, String message, Throwable exception) { 86 | logFileHandle.writeString("\n" + new Timestamp(System.currentTimeMillis()) + "\n" + retrieveSystemDetails() + tag + ": " + message + "\n", true); 87 | printException(exception); 88 | 89 | System.out.println(tag + ": " + message); 90 | exception.printStackTrace(System.out); 91 | } 92 | 93 | private void printException(Throwable exception) { 94 | PrintStream printStream = new PrintStream(logFileHandle.write(true)); 95 | exception.printStackTrace(printStream); 96 | printStream.close(); 97 | } 98 | 99 | private String retrieveSystemDetails() { 100 | return "Version: " + Core.version + 101 | "\nOS: " + (UIUtils.isMac ? "Mac" : UIUtils.isWindows ? "Windows" : UIUtils.isLinux ? "Linux" : "Other") + "\n"; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/lwjgl3/Lwjgl3Launcher.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.lwjgl3; 2 | 3 | import com.badlogic.gdx.Gdx; 4 | import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application; 5 | import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration; 6 | import com.ray3k.gdxparticleeditor.Core; 7 | 8 | public class Lwjgl3Launcher { 9 | public static void main(String[] args) { 10 | if (StartupHelper.startNewJvmIfRequired()) return; 11 | try { 12 | createApplication(); 13 | } catch (Throwable throwable) { 14 | Gdx.app.error(Lwjgl3Launcher.class.getName(), "Error while running application", throwable); 15 | } 16 | } 17 | 18 | private static Lwjgl3Application createApplication() { 19 | var core = new Core(); 20 | return new Lwjgl3Application(core, getDefaultConfiguration()); 21 | } 22 | 23 | private static Lwjgl3ApplicationConfiguration getDefaultConfiguration() { 24 | Lwjgl3ApplicationConfiguration configuration = new Lwjgl3ApplicationConfiguration(); 25 | configuration.setTitle(Core.DEFAULT_WINDOW_TITLE); 26 | configuration.useVsync(true); 27 | configuration.setForegroundFPS(Lwjgl3ApplicationConfiguration.getDisplayMode().refreshRate); 28 | configuration.setBackBufferConfig(8, 8, 8, 8, 16, 0, 10); 29 | configuration.setWindowedMode(1000, 950); 30 | configuration.setWindowIcon("icon128.png", "icon64.png", "icon32.png", "icon16.png"); 31 | configuration.setWindowListener(Core.windowListener); 32 | return configuration; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/runnables/ExportRunnable.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.runnables; 2 | 3 | import com.badlogic.gdx.Gdx; 4 | import com.badlogic.gdx.files.FileHandle; 5 | import com.badlogic.gdx.utils.GdxRuntimeException; 6 | import com.badlogic.gdx.utils.StreamUtils; 7 | import com.ray3k.gdxparticleeditor.Core; 8 | import com.ray3k.gdxparticleeditor.Settings; 9 | import com.ray3k.gdxparticleeditor.widgets.poptables.PopError; 10 | import lombok.Getter; 11 | import lombok.Setter; 12 | 13 | import java.io.FileWriter; 14 | import java.io.IOException; 15 | import java.io.Writer; 16 | 17 | import static com.ray3k.gdxparticleeditor.Core.*; 18 | import static com.ray3k.gdxparticleeditor.Settings.*; 19 | import static com.ray3k.gdxparticleeditor.Utils.showToast; 20 | import static com.ray3k.gdxparticleeditor.widgets.panels.EffectEmittersPanel.effectEmittersPanel; 21 | 22 | public class ExportRunnable implements Runnable { 23 | 24 | @Getter @Setter 25 | private Runnable onCompletionRunnable; 26 | 27 | @Getter @Setter 28 | private FileHandle exportFileHandle; 29 | 30 | @Getter @Setter 31 | private boolean exportImages; 32 | 33 | @Override 34 | public void run() { 35 | if (preferences.getBoolean(NAME_PRESUME_FILE_EXTENSION, DEFAULT_PRESUME_FILE_EXTENSION) && !exportFileHandle.extension().equals("p")) 36 | exportFileHandle = exportFileHandle.sibling(exportFileHandle.name() + ".p"); 37 | 38 | Settings.setDefaultSavePath(exportFileHandle.parent()); 39 | 40 | //enable all emitters for export 41 | particleEffect.getEmitters().clear(); 42 | for (var entry : activeEmitters.entries()) { 43 | particleEffect.getEmitters().add(entry.key); 44 | } 45 | 46 | var fileError = false; 47 | Writer fileWriter = null; 48 | try { 49 | fileWriter = new FileWriter(exportFileHandle.file()); 50 | particleEffect.save(fileWriter); 51 | } catch (IOException e) { 52 | var error = "Error saving particle file."; 53 | var pop = new PopError(error, e.getMessage()); 54 | pop.show(foregroundStage); 55 | 56 | Gdx.app.error(Core.class.getName(), error, e); 57 | fileError = true; 58 | } finally { 59 | StreamUtils.closeQuietly(fileWriter); 60 | } 61 | 62 | if (exportImages) 63 | for (var fileHandle : fileHandles.values()) { 64 | if (fileHandle.parent().equals(exportFileHandle.parent())) continue; 65 | try { 66 | fileHandle.copyTo(exportFileHandle.parent().child(fileHandle.name())); 67 | } catch (GdxRuntimeException e) { 68 | var error = "Error copying files to save location."; 69 | var pop = new PopError(error, e.getMessage()); 70 | pop.show(foregroundStage); 71 | 72 | Gdx.app.error(Core.class.getName(), error, e); 73 | fileError = true; 74 | } 75 | } 76 | 77 | //reset enabled/disabled emitters 78 | particleEffect.getEmitters().clear(); 79 | for (var entry : activeEmitters.entries()) { 80 | if (entry.value) particleEffect.getEmitters().add(entry.key); 81 | } 82 | 83 | effectEmittersPanel.hidePopEmitterControls(); 84 | showToast("Exported " + exportFileHandle.name()); 85 | 86 | if (!fileError) { 87 | if (onCompletionRunnable != null) Gdx.app.postRunnable(onCompletionRunnable); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/runnables/ImagesRunnable.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.runnables; 2 | 3 | import com.badlogic.gdx.Gdx; 4 | import com.badlogic.gdx.files.FileHandle; 5 | import com.badlogic.gdx.scenes.scene2d.Touchable; 6 | import com.badlogic.gdx.scenes.scene2d.utils.UIUtils; 7 | import com.badlogic.gdx.utils.Array; 8 | import com.ray3k.gdxparticleeditor.FileDialogs; 9 | import com.ray3k.gdxparticleeditor.undo.UndoManager; 10 | import com.ray3k.gdxparticleeditor.undo.undoables.ImagesAddUndoable; 11 | import com.ray3k.gdxparticleeditor.widgets.subpanels.ImagesSubPanel; 12 | 13 | import java.util.concurrent.ExecutorService; 14 | import java.util.concurrent.Executors; 15 | 16 | import static com.ray3k.gdxparticleeditor.Core.selectedEmitter; 17 | import static com.ray3k.gdxparticleeditor.Core.stage; 18 | import static com.ray3k.gdxparticleeditor.Settings.getDefaultImagePath; 19 | import static com.ray3k.gdxparticleeditor.Settings.setDefaultImagePath; 20 | import static com.ray3k.gdxparticleeditor.Utils.showToast; 21 | import static com.ray3k.gdxparticleeditor.widgets.panels.EffectEmittersPanel.effectEmittersPanel; 22 | import static com.ray3k.gdxparticleeditor.widgets.panels.EmitterPropertiesPanel.emitterPropertiesPanel; 23 | import static com.ray3k.gdxparticleeditor.widgets.subpanels.ImagesSubPanel.imagesSubPanel; 24 | 25 | public class ImagesRunnable implements Runnable { 26 | 27 | private static boolean open; 28 | 29 | @Override 30 | public void run () { 31 | if (open) 32 | return; 33 | 34 | if (UIUtils.isMac) openWindow(); 35 | else { 36 | ExecutorService service = Executors.newSingleThreadExecutor(); 37 | service.execute(this::openWindow); 38 | service.shutdown(); 39 | } 40 | } 41 | 42 | private void openWindow() { 43 | open = true; 44 | stage.getRoot().setTouchable(Touchable.disabled); 45 | 46 | if (effectEmittersPanel == null || emitterPropertiesPanel == null) return; 47 | 48 | var selectedFileHandles = FileDialogs.openMultipleDialog("Add images", getDefaultImagePath(), new String[] {"png","jpg","jpeg"}, "Image files (*.png;*.jpg;*.jpeg)"); 49 | if (selectedFileHandles == null) { 50 | stage.getRoot().setTouchable(Touchable.enabled); 51 | open = false; 52 | return; 53 | } 54 | 55 | if (selectedFileHandles.size > 0) { 56 | setDefaultImagePath(selectedFileHandles.first().parent()); 57 | Gdx.app.postRunnable(() -> { 58 | loadOnMainThread(selectedFileHandles); 59 | }); 60 | } 61 | 62 | stage.getRoot().setTouchable(Touchable.enabled); 63 | open = false; 64 | } 65 | 66 | private void loadOnMainThread (Array selectedFileHandles) { 67 | UndoManager.add(new ImagesAddUndoable(selectedEmitter, ImagesSubPanel.imagesSubPanel.list.getSelectedIndex() + 1, selectedFileHandles, "Add Images")); 68 | if (selectedFileHandles.size > 0) { 69 | imagesSubPanel.updateList(); 70 | imagesSubPanel.updateDisabled(); 71 | 72 | showToast(selectedFileHandles.size == 1 ? "Added image " + selectedFileHandles.first().name() : "Added " + selectedFileHandles.size + " images"); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/runnables/MergeRunnable.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.runnables; 2 | 3 | import com.badlogic.gdx.Gdx; 4 | import com.badlogic.gdx.files.FileHandle; 5 | import com.badlogic.gdx.scenes.scene2d.Touchable; 6 | import com.badlogic.gdx.scenes.scene2d.utils.UIUtils; 7 | import com.badlogic.gdx.utils.ObjectMap; 8 | import com.badlogic.gdx.utils.OrderedMap; 9 | import com.ray3k.gdxparticleeditor.FileDialogs; 10 | import com.ray3k.gdxparticleeditor.Settings; 11 | import com.ray3k.gdxparticleeditor.Utils; 12 | import com.ray3k.gdxparticleeditor.undo.UndoManager; 13 | import com.ray3k.gdxparticleeditor.undo.undoables.MergeEmitterUndoable; 14 | 15 | import java.util.concurrent.ExecutorService; 16 | import java.util.concurrent.Executors; 17 | 18 | import static com.ray3k.gdxparticleeditor.Core.*; 19 | import static com.ray3k.gdxparticleeditor.Settings.*; 20 | import static com.ray3k.gdxparticleeditor.Utils.showToast; 21 | import static com.ray3k.gdxparticleeditor.widgets.panels.EffectEmittersPanel.effectEmittersPanel; 22 | import static com.ray3k.gdxparticleeditor.widgets.panels.EmitterPropertiesPanel.emitterPropertiesPanel; 23 | 24 | public class MergeRunnable implements Runnable { 25 | 26 | private static boolean open; 27 | 28 | @Override 29 | public void run () { 30 | if (open) 31 | return; 32 | 33 | if (UIUtils.isMac) openWindow(); 34 | else { 35 | ExecutorService service = Executors.newSingleThreadExecutor(); 36 | service.execute(this::openWindow); 37 | service.shutdown(); 38 | } 39 | } 40 | 41 | private void openWindow() { 42 | open = true; 43 | stage.getRoot().setTouchable(Touchable.disabled); 44 | 45 | if (effectEmittersPanel == null || emitterPropertiesPanel == null) return; 46 | 47 | var useFileExtension = preferences.getBoolean(NAME_PRESUME_FILE_EXTENSION, DEFAULT_PRESUME_FILE_EXTENSION); 48 | var filterPatterns = useFileExtension ? new String[] {"p"} : null; 49 | var fileHandle = FileDialogs.openDialog("Merge", getDefaultSavePath(), filterPatterns, "Particle files (*.p)"); 50 | 51 | if (fileHandle != null) { 52 | defaultFileName = fileHandle.name(); 53 | Settings.setDefaultSavePath(fileHandle.parent()); 54 | Gdx.app.postRunnable(() -> { 55 | loadOnMainThread(fileHandle); 56 | }); 57 | } 58 | 59 | stage.getRoot().setTouchable(Touchable.enabled); 60 | open = false; 61 | } 62 | 63 | private void loadOnMainThread (FileHandle fileHandle) { 64 | 65 | var oldActiveEmitters = new OrderedMap<>(activeEmitters); 66 | var oldFileHandles = new ObjectMap<>(fileHandles); 67 | var oldSprites = new ObjectMap<>(sprites); 68 | var oldSelectedIndex = activeEmitters.keys().toArray().indexOf(selectedEmitter, true); 69 | 70 | var completed = Utils.mergeParticle(fileHandle); 71 | if (!completed) return; 72 | 73 | UndoManager.add(MergeEmitterUndoable 74 | .builder() 75 | .oldActiveEmitters(oldActiveEmitters) 76 | .oldFileHandles(oldFileHandles) 77 | .oldSprites(oldSprites) 78 | .oldSelectedIndex(oldSelectedIndex) 79 | .newActiveEmitters(new OrderedMap<>(activeEmitters)) 80 | .newFileHandles(new ObjectMap<>(fileHandles)) 81 | .newSprites(new ObjectMap<>(sprites)) 82 | .newSelectedIndex(activeEmitters.keys().toArray().indexOf(selectedEmitter, true)) 83 | .description("Merge Particle Effect") 84 | .build()); 85 | effectEmittersPanel.populateEmitters(); 86 | effectEmittersPanel.updateDisableableWidgets(); 87 | effectEmittersPanel.hidePopEmitterControls(); 88 | emitterPropertiesPanel.populateScrollTable(null); 89 | showToast("Merged " + fileHandle.name()); 90 | 91 | updateWindowTitle(); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/runnables/OpenRunnable.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.runnables; 2 | 3 | import com.badlogic.gdx.Gdx; 4 | import com.badlogic.gdx.files.FileHandle; 5 | import com.badlogic.gdx.scenes.scene2d.Touchable; 6 | import com.badlogic.gdx.scenes.scene2d.utils.UIUtils; 7 | import com.ray3k.gdxparticleeditor.FileDialogs; 8 | import com.ray3k.gdxparticleeditor.Settings; 9 | import com.ray3k.gdxparticleeditor.Utils; 10 | import com.ray3k.gdxparticleeditor.undo.UndoManager; 11 | import com.ray3k.gdxparticleeditor.widgets.poptables.PopConfirmLoad; 12 | 13 | import java.util.concurrent.ExecutorService; 14 | import java.util.concurrent.Executors; 15 | 16 | import static com.ray3k.gdxparticleeditor.Core.*; 17 | import static com.ray3k.gdxparticleeditor.Settings.*; 18 | import static com.ray3k.gdxparticleeditor.Utils.showToast; 19 | import static com.ray3k.gdxparticleeditor.widgets.panels.EffectEmittersPanel.effectEmittersPanel; 20 | import static com.ray3k.gdxparticleeditor.widgets.panels.EmitterPropertiesPanel.emitterPropertiesPanel; 21 | 22 | public class OpenRunnable implements Runnable { 23 | 24 | private static boolean open; 25 | 26 | @Override 27 | public void run () { 28 | if (open) 29 | return; 30 | 31 | if (unsavedChangesMade) { 32 | var saveFirstRunnable = new SaveRunnable(); 33 | var saveAsFirstRunnable = new SaveAsRunnable(); 34 | saveFirstRunnable.setSaveAsRunnable(saveAsFirstRunnable); 35 | saveAsFirstRunnable.setSaveRunnable(saveFirstRunnable); 36 | saveFirstRunnable.setOnCompletionRunnable(this); 37 | 38 | var pop = new PopConfirmLoad(saveFirstRunnable, () -> { 39 | unsavedChangesMade = false; 40 | OpenRunnable.this.run(); 41 | }); 42 | pop.show(foregroundStage); 43 | return; 44 | } 45 | 46 | if (UIUtils.isMac) openWindow(); 47 | else { 48 | ExecutorService service = Executors.newSingleThreadExecutor(); 49 | service.execute(this::openWindow); 50 | service.shutdown(); 51 | } 52 | } 53 | 54 | private void openWindow() { 55 | open = true; 56 | stage.getRoot().setTouchable(Touchable.disabled); 57 | 58 | if (effectEmittersPanel == null || emitterPropertiesPanel == null) 59 | return; 60 | 61 | var useFileExtension = preferences.getBoolean(NAME_PRESUME_FILE_EXTENSION, DEFAULT_PRESUME_FILE_EXTENSION); 62 | var filterPatterns = useFileExtension ? new String[] {"p"} : null; 63 | var fileHandle = FileDialogs.openDialog("Open", getDefaultSavePath(), filterPatterns, "Particle files (*.p)"); 64 | 65 | if (fileHandle != null) { 66 | defaultFileName = fileHandle.name(); 67 | Settings.setDefaultSavePath(fileHandle.parent()); 68 | Gdx.app.postRunnable(() -> { 69 | loadOnMainThread(fileHandle); 70 | }); 71 | 72 | openFileFileHandle = fileHandle; 73 | } 74 | 75 | stage.getRoot().setTouchable(Touchable.enabled); 76 | open = false; 77 | } 78 | 79 | private void loadOnMainThread (FileHandle fileHandle) { 80 | var completed = Utils.loadParticle(fileHandle); 81 | 82 | if (!completed) return; 83 | 84 | selectedEmitter = particleEffect.getEmitters().first(); 85 | 86 | effectEmittersPanel.populateEmitters(); 87 | effectEmittersPanel.updateDisableableWidgets(); 88 | emitterPropertiesPanel.populateScrollTable(null); 89 | effectEmittersPanel.hidePopEmitterControls(); 90 | showToast("Opened " + fileHandle.name()); 91 | 92 | UndoManager.clear(); 93 | unsavedChangesMade = false; 94 | allowClose = true; 95 | updateWindowTitle(); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/runnables/RedoShortcutRunnable.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.runnables; 2 | 3 | import com.ray3k.gdxparticleeditor.undo.UndoManager; 4 | 5 | public class RedoShortcutRunnable implements Runnable { 6 | 7 | @Override 8 | public void run () { 9 | if (UndoManager.hasRedo()) { 10 | UndoManager.redo(); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/runnables/ReloadImagesRunnable.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.runnables; 2 | 3 | import com.ray3k.gdxparticleeditor.Utils; 4 | 5 | public class ReloadImagesRunnable implements Runnable { 6 | 7 | @Override 8 | public void run () { 9 | Utils.reloadSprites(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/runnables/SaveAsRunnable.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.runnables; 2 | 3 | import com.badlogic.gdx.scenes.scene2d.Touchable; 4 | import com.badlogic.gdx.scenes.scene2d.utils.UIUtils; 5 | import com.ray3k.gdxparticleeditor.FileDialogs; 6 | 7 | import java.util.concurrent.ExecutorService; 8 | import java.util.concurrent.Executors; 9 | 10 | import static com.ray3k.gdxparticleeditor.Core.*; 11 | import static com.ray3k.gdxparticleeditor.Settings.*; 12 | 13 | public class SaveAsRunnable implements Runnable { 14 | 15 | private static boolean open; 16 | private SaveRunnable saveRunnable; 17 | 18 | public void setSaveRunnable (SaveRunnable runnable) { 19 | saveRunnable = runnable; 20 | } 21 | 22 | @Override 23 | public void run () { 24 | if (open) return; 25 | 26 | if (UIUtils.isMac) openWindow(); 27 | else { 28 | ExecutorService service = Executors.newSingleThreadExecutor(); 29 | service.execute(this::openWindow); 30 | service.shutdown(); 31 | } 32 | } 33 | 34 | private void openWindow() { 35 | open = true; 36 | stage.getRoot().setTouchable(Touchable.disabled); 37 | 38 | var useFileExtension = preferences.getBoolean(NAME_PRESUME_FILE_EXTENSION, DEFAULT_PRESUME_FILE_EXTENSION); 39 | var filterPatterns = useFileExtension ? new String[] {"p"} : null; 40 | var saveHandle = FileDialogs.saveDialog("Save", getDefaultSavePath(), defaultFileName, filterPatterns, "Particle Files (*.p)"); 41 | 42 | if (saveHandle != null) { 43 | openFileFileHandle = saveHandle; 44 | saveRunnable.run(); 45 | } 46 | 47 | stage.getRoot().setTouchable(Touchable.enabled); 48 | open = false; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/runnables/SaveRunnable.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.runnables; 2 | 3 | import com.badlogic.gdx.Gdx; 4 | import com.badlogic.gdx.scenes.scene2d.Event; 5 | import com.badlogic.gdx.utils.GdxRuntimeException; 6 | import com.badlogic.gdx.utils.StreamUtils; 7 | import com.ray3k.gdxparticleeditor.Core; 8 | import com.ray3k.gdxparticleeditor.Settings; 9 | import com.ray3k.gdxparticleeditor.widgets.poptables.PopError; 10 | import com.ray3k.stripe.PopTable.TableShowHideListener; 11 | import lombok.Getter; 12 | import lombok.Setter; 13 | 14 | import java.io.FileWriter; 15 | import java.io.IOException; 16 | import java.io.Writer; 17 | 18 | import static com.ray3k.gdxparticleeditor.Core.*; 19 | import static com.ray3k.gdxparticleeditor.Settings.*; 20 | import static com.ray3k.gdxparticleeditor.Utils.showToast; 21 | import static com.ray3k.gdxparticleeditor.widgets.panels.EffectEmittersPanel.effectEmittersPanel; 22 | 23 | public class SaveRunnable implements Runnable { 24 | 25 | private SaveAsRunnable saveAsRunnable; 26 | @Getter @Setter 27 | private Runnable onCompletionRunnable; 28 | 29 | public void setSaveAsRunnable (SaveAsRunnable runnable) { 30 | saveAsRunnable = runnable; 31 | } 32 | 33 | @Override 34 | public void run () { 35 | if (openFileFileHandle != null) { 36 | if (preferences.getBoolean(NAME_PRESUME_FILE_EXTENSION, DEFAULT_PRESUME_FILE_EXTENSION) && !openFileFileHandle.extension().equals("p")) 37 | openFileFileHandle = openFileFileHandle.sibling(openFileFileHandle.name() + ".p"); 38 | 39 | Settings.setDefaultSavePath(openFileFileHandle.parent()); 40 | defaultFileName = openFileFileHandle.name(); 41 | 42 | //enable all emitters for export 43 | particleEffect.getEmitters().clear(); 44 | for (var entry : activeEmitters.entries()) { 45 | particleEffect.getEmitters().add(entry.key); 46 | } 47 | 48 | var fileError = false; 49 | Writer fileWriter = null; 50 | try { 51 | fileWriter = new FileWriter(openFileFileHandle.file()); 52 | particleEffect.save(fileWriter); 53 | } catch (IOException e) { 54 | var error = "Error saving particle file."; 55 | var pop = new PopError(error, e.getMessage()); 56 | pop.show(foregroundStage); 57 | 58 | Gdx.app.error(Core.class.getName(), error, e); 59 | fileError = true; 60 | } finally { 61 | StreamUtils.closeQuietly(fileWriter); 62 | } 63 | 64 | if (preferences.getBoolean(NAME_EXPORT_IMAGES, DEFAULT_EXPORT_IMAGES)) for (var fileHandle : fileHandles.values()) { 65 | if (fileHandle.parent().equals(openFileFileHandle.parent())) continue; 66 | try { 67 | fileHandle.copyTo(openFileFileHandle.parent().child(fileHandle.name())); 68 | } catch (GdxRuntimeException e) { 69 | var error = "Error copying files to save location."; 70 | var pop = new PopError(error, e.getMessage()); 71 | pop.show(foregroundStage); 72 | 73 | Gdx.app.error(Core.class.getName(), error, e); 74 | fileError = true; 75 | } 76 | } 77 | 78 | //reset enabled/disabled emitters 79 | particleEffect.getEmitters().clear(); 80 | for (var entry : activeEmitters.entries()) { 81 | if (entry.value) particleEffect.getEmitters().add(entry.key); 82 | } 83 | 84 | effectEmittersPanel.hidePopEmitterControls(); 85 | showToast("Saved " + openFileFileHandle.name()); 86 | 87 | if (!fileError) { 88 | unsavedChangesMade = false; 89 | allowClose = true; 90 | Core.updateWindowTitle(); 91 | if (onCompletionRunnable != null) Gdx.app.postRunnable(onCompletionRunnable); 92 | } 93 | } else { 94 | saveAsRunnable.run(); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/runnables/SwitchModeRunnable.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.runnables; 2 | 3 | import com.badlogic.gdx.utils.Align; 4 | import com.ray3k.gdxparticleeditor.Core; 5 | import com.ray3k.gdxparticleeditor.Settings; 6 | import com.ray3k.gdxparticleeditor.shortcuts.ShortcutManager; 7 | import com.ray3k.gdxparticleeditor.widgets.tables.ClassicTable; 8 | import com.ray3k.gdxparticleeditor.widgets.tables.WizardTable; 9 | 10 | import static com.ray3k.gdxparticleeditor.Core.shortcutManager; 11 | import static com.ray3k.gdxparticleeditor.PresetActions.transition; 12 | 13 | public class SwitchModeRunnable implements Runnable { 14 | 15 | @Override 16 | public void run () { 17 | if (Core.openTable.equals("Wizard") && (ClassicTable.classicTable == null || ClassicTable.classicTable.getStage() == null)) { 18 | Core.openTable = "Classic"; 19 | transition(WizardTable.wizardTable, new ClassicTable(), Align.top); 20 | shortcutManager.setDisabled(false); 21 | ShortcutManager.setScope(Settings.CLASSIC_SCOPE); 22 | } else if (Core.openTable.equals("Classic") && (WizardTable.wizardTable == null || WizardTable.wizardTable.getStage() == null)) { 23 | Core.openTable = "Wizard"; 24 | transition(ClassicTable.classicTable, new WizardTable(), Align.top); 25 | shortcutManager.setDisabled(false); 26 | ShortcutManager.setScope(Settings.WIZARD_SCOPE); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/runnables/UndoShortcutRunnable.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.runnables; 2 | 3 | import com.ray3k.gdxparticleeditor.undo.UndoManager; 4 | 5 | public class UndoShortcutRunnable implements Runnable { 6 | 7 | @Override 8 | public void run () { 9 | if (UndoManager.hasUndo()) { 10 | UndoManager.undo(); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/shortcuts/InvalidShortcutException.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.shortcuts; 2 | 3 | /** 4 | * An exception thrown when the user enters an invalid key combination. 5 | */ 6 | public class InvalidShortcutException extends RuntimeException { 7 | 8 | public InvalidShortcutException (String message) { 9 | super(message); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/shortcuts/KeybindReference.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.shortcuts; 2 | 3 | import static com.ray3k.gdxparticleeditor.Utils.EMPTY_KEYBIND; 4 | 5 | /** 6 | * A pair of integer arrays that hold a primary and secondary keybind for a shortcut. 7 | */ 8 | public class KeybindReference { 9 | 10 | private final int[] primaryKeybind; 11 | private final int[] secondaryKeybind; 12 | 13 | public KeybindReference (int[] primaryKeybind, int[] secondaryKeybind) { 14 | this.primaryKeybind = primaryKeybind; 15 | this.secondaryKeybind = secondaryKeybind; 16 | } 17 | 18 | public int[] getPrimaryKeybind() { 19 | return primaryKeybind != null ? primaryKeybind : EMPTY_KEYBIND; 20 | } 21 | 22 | public int[] getSecondaryKeybind() { 23 | return secondaryKeybind != null ? secondaryKeybind : EMPTY_KEYBIND; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/shortcuts/Shortcut.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.shortcuts; 2 | 3 | import java.util.Objects; 4 | 5 | import static com.ray3k.gdxparticleeditor.Utils.EMPTY_KEYBIND; 6 | 7 | /** 8 | * A Shortcut consists of a primary and secondary keybind associated with a Runnable action. A name and description 9 | * are stored for display in the settings UI. 10 | */ 11 | public class Shortcut { 12 | 13 | private int[] primaryKeybind; 14 | private int[] secondaryKeybind; 15 | private int primaryKeybindPacked; 16 | private int secondaryKeybindPacked; 17 | private final Runnable runnable; 18 | private final String name; 19 | private final String description; 20 | private int scope; 21 | 22 | public Shortcut (String name, String description, Runnable runnable) { 23 | this.name = name; 24 | this.description = description; 25 | this.runnable = runnable; 26 | } 27 | 28 | public Shortcut setScope (int scope) { 29 | this.scope = scope; 30 | return this; 31 | } 32 | 33 | public int getScope() { 34 | return scope; 35 | } 36 | 37 | public Runnable getRunnable() { 38 | return runnable; 39 | } 40 | 41 | public String getName() { 42 | return name; 43 | } 44 | 45 | public String getDescription() { 46 | return description; 47 | } 48 | 49 | public Shortcut setPrimaryKeybind(int[] keybind, int packed) { 50 | primaryKeybind = keybind; 51 | primaryKeybindPacked = packed; 52 | return this; 53 | } 54 | public int[] getPrimaryKeybind() { 55 | return primaryKeybind != null ? primaryKeybind : EMPTY_KEYBIND; 56 | } 57 | 58 | public int getPrimaryKeybindPacked () { 59 | return primaryKeybindPacked; 60 | } 61 | 62 | public Shortcut setSecondaryKeybind (int[] keybind, int packed) { 63 | secondaryKeybind = keybind; 64 | secondaryKeybindPacked = packed; 65 | return this; 66 | } 67 | 68 | public int[] getSecondaryKeybind() { 69 | return secondaryKeybind != null ? secondaryKeybind : EMPTY_KEYBIND; 70 | } 71 | 72 | public int getSecondaryKeybindPacked() { 73 | return secondaryKeybindPacked; 74 | } 75 | 76 | @Override 77 | public boolean equals (Object o) { 78 | if (this == o) 79 | return true; 80 | if (o == null || getClass() != o.getClass()) 81 | return false; 82 | Shortcut shortcut = (Shortcut)o; 83 | return scope == shortcut.scope && runnable.equals(shortcut.runnable) && name.equals(shortcut.name) && description.equals( 84 | shortcut.description); 85 | } 86 | 87 | @Override 88 | public int hashCode () { 89 | return Objects.hash(runnable, name, description, scope); 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/undo/UndoManager.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.undo; 2 | 3 | import com.badlogic.gdx.utils.Array; 4 | import com.ray3k.gdxparticleeditor.Core; 5 | import com.ray3k.gdxparticleeditor.Utils; 6 | 7 | import static com.ray3k.gdxparticleeditor.Core.allowClose; 8 | 9 | /** 10 | * The UndoManager is a system to handle undo/redo in the app. The description of the currently primed undo/redo is 11 | * provided. The undoIndex indicates where in the history that the user is currently at. Certain actions will wipe the 12 | * undo history, such as loading a new effect. Adding a new undoable will erase all undoables in the history past the 13 | * current undoIndex. 14 | */ 15 | public class UndoManager { 16 | public final static Array undoables = new Array<>(); 17 | public static int undoIndex = -1; 18 | 19 | public static void add(Undoable undoable) { 20 | if (hasRedo()) undoables.removeRange(undoIndex + 1, undoables.size - 1); 21 | 22 | undoables.add(undoable); 23 | undoable.start(); 24 | undoIndex = undoables.size - 1; 25 | Utils.refreshUndoButtons(); 26 | 27 | Core.unsavedChangesMade = true; 28 | allowClose = false; 29 | Core.updateWindowTitle(); 30 | } 31 | 32 | public static String getUndoDescription() { 33 | return hasUndo() ? undoables.get(undoIndex).getDescription() : ""; 34 | } 35 | 36 | public static String getRedoDescription() { 37 | return hasRedo() ? undoables.get(undoIndex + 1).getDescription() : ""; 38 | } 39 | 40 | public static boolean hasUndo() { 41 | return undoIndex >= 0; 42 | } 43 | 44 | public static boolean hasRedo() { 45 | return undoables.size > 0 && undoIndex < undoables.size - 1; 46 | } 47 | 48 | public static void undo() { 49 | if (!hasUndo()) return; 50 | var undoable = undoables.get(undoIndex--); 51 | Utils.showToast("Undo " + undoable.getDescription()); 52 | undoable.undo(); 53 | Utils.refreshUndoButtons(); 54 | Core.unsavedChangesMade = true; 55 | allowClose = false; 56 | Core.updateWindowTitle(); 57 | } 58 | 59 | public static void redo() { 60 | if (!hasRedo()) return; 61 | var undoable = undoables.get(++undoIndex); 62 | Utils.showToast("Redo " + undoable.getDescription()); 63 | undoable.redo(); 64 | Utils.refreshUndoButtons(); 65 | Core.unsavedChangesMade = true; 66 | allowClose = false; 67 | Core.updateWindowTitle(); 68 | } 69 | 70 | public static void clear() { 71 | undoables.clear(); 72 | undoIndex = -1; 73 | Utils.refreshUndoButtons(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/undo/Undoable.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.undo; 2 | 3 | /** 4 | * Undoable is an interface used by the UndoManager to allow the user to undo/redo actions in the interface. This 5 | * interface also provides the description when the user hovers over the undo/redo buttons. 6 | */ 7 | public interface Undoable { 8 | void undo(); 9 | void redo(); 10 | void start(); 11 | String getDescription(); 12 | } 13 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/undo/undoables/CountMaxUndoable.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.undo.undoables; 2 | 3 | import com.badlogic.gdx.graphics.g2d.ParticleEmitter; 4 | import com.ray3k.gdxparticleeditor.undo.Undoable; 5 | import lombok.AllArgsConstructor; 6 | 7 | import static com.ray3k.gdxparticleeditor.Core.particleEffect; 8 | import static com.ray3k.gdxparticleeditor.Core.selectedEmitter; 9 | import static com.ray3k.gdxparticleeditor.widgets.panels.EffectEmittersPanel.effectEmittersPanel; 10 | import static com.ray3k.gdxparticleeditor.widgets.panels.EmitterPropertiesPanel.emitterPropertiesPanel; 11 | 12 | /** 13 | * Undoable for increasing the maximum particle count for the emitter. 14 | */ 15 | @AllArgsConstructor 16 | public class CountMaxUndoable implements Undoable { 17 | private ParticleEmitter emitter; 18 | private int newValue; 19 | private int oldValue; 20 | 21 | @Override 22 | public void undo() { 23 | selectedEmitter = emitter; 24 | 25 | emitter.setMaxParticleCount(oldValue); 26 | refreshDisplay(); 27 | } 28 | 29 | @Override 30 | public void redo() { 31 | selectedEmitter = emitter; 32 | emitter.setMaxParticleCount(newValue); 33 | refreshDisplay(); 34 | } 35 | 36 | @Override 37 | public void start() { 38 | emitter.setMaxParticleCount(newValue); 39 | } 40 | 41 | @Override 42 | public String getDescription() { 43 | return "change Count max"; 44 | } 45 | 46 | private void refreshDisplay() { 47 | effectEmittersPanel.populateEmitters(); 48 | emitterPropertiesPanel.populateScrollTable(null); 49 | particleEffect.reset(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/undo/undoables/CountMinUndoable.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.undo.undoables; 2 | 3 | import com.badlogic.gdx.graphics.g2d.ParticleEmitter; 4 | import com.ray3k.gdxparticleeditor.undo.Undoable; 5 | import lombok.AllArgsConstructor; 6 | 7 | import static com.ray3k.gdxparticleeditor.Core.particleEffect; 8 | import static com.ray3k.gdxparticleeditor.Core.selectedEmitter; 9 | import static com.ray3k.gdxparticleeditor.widgets.panels.EffectEmittersPanel.effectEmittersPanel; 10 | import static com.ray3k.gdxparticleeditor.widgets.panels.EmitterPropertiesPanel.emitterPropertiesPanel; 11 | 12 | /** 13 | * Undoable for increasing the minimum particle count for the emitter. 14 | */ 15 | @AllArgsConstructor 16 | public class CountMinUndoable implements Undoable { 17 | private ParticleEmitter emitter; 18 | private int newValue; 19 | private int oldValue; 20 | 21 | @Override 22 | public void undo() { 23 | selectedEmitter = emitter; 24 | 25 | emitter.setMinParticleCount(oldValue); 26 | refreshDisplay(); 27 | } 28 | 29 | @Override 30 | public void redo() { 31 | selectedEmitter = emitter; 32 | 33 | emitter.setMinParticleCount(newValue); 34 | refreshDisplay(); 35 | } 36 | 37 | @Override 38 | public void start() { 39 | emitter.setMinParticleCount(newValue); 40 | } 41 | 42 | @Override 43 | public String getDescription() { 44 | return "change Count min"; 45 | } 46 | 47 | private void refreshDisplay() { 48 | effectEmittersPanel.populateEmitters(); 49 | emitterPropertiesPanel.populateScrollTable(null); 50 | particleEffect.reset(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/undo/undoables/DeleteEmitterUndoable.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.undo.undoables; 2 | 3 | import com.badlogic.gdx.graphics.g2d.ParticleEmitter; 4 | import com.ray3k.gdxparticleeditor.undo.Undoable; 5 | import lombok.AllArgsConstructor; 6 | 7 | import static com.ray3k.gdxparticleeditor.Core.*; 8 | import static com.ray3k.gdxparticleeditor.widgets.panels.EffectEmittersPanel.effectEmittersPanel; 9 | import static com.ray3k.gdxparticleeditor.widgets.panels.EmitterPropertiesPanel.emitterPropertiesPanel; 10 | 11 | /** 12 | * Undoable to delete the selected emitter. 13 | */ 14 | @AllArgsConstructor 15 | public class DeleteEmitterUndoable implements Undoable { 16 | private ParticleEmitter emitter; 17 | private int index; 18 | private String description; 19 | 20 | @Override 21 | public void undo() { 22 | particleEffect.getEmitters().add(emitter); 23 | activeEmitters.put(emitter, true); 24 | activeEmitters.orderedKeys().removeIndex(activeEmitters.orderedKeys().size - 1); 25 | activeEmitters.orderedKeys().insert(index, emitter); 26 | selectedEmitter = emitter; 27 | refreshDisplay(); 28 | } 29 | 30 | @Override 31 | public void redo() { 32 | particleEffect.getEmitters().removeValue(emitter, true); 33 | activeEmitters.remove(emitter); 34 | selectedEmitter = activeEmitters.orderedKeys().get(Math.min(index, activeEmitters.orderedKeys().size - 1)); 35 | refreshDisplay(); 36 | } 37 | 38 | @Override 39 | public void start() { 40 | particleEffect.getEmitters().removeValue(emitter, true); 41 | activeEmitters.remove(emitter); 42 | selectedEmitter = activeEmitters.orderedKeys().get(Math.min(index, activeEmitters.orderedKeys().size - 1)); 43 | } 44 | 45 | @Override 46 | public String getDescription() { 47 | return description; 48 | } 49 | 50 | private void refreshDisplay() { 51 | effectEmittersPanel.populateEmitters(); 52 | effectEmittersPanel.updateDisableableWidgets(); 53 | emitterPropertiesPanel.populateScrollTable(null); 54 | particleEffect.reset(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/undo/undoables/DualScaledNumericValueRelativeUndoable.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.undo.undoables; 2 | 3 | import com.badlogic.gdx.graphics.g2d.ParticleEmitter; 4 | import com.badlogic.gdx.graphics.g2d.ParticleEmitter.ScaledNumericValue; 5 | import com.ray3k.gdxparticleeditor.undo.Undoable; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Data; 8 | 9 | import static com.ray3k.gdxparticleeditor.Core.particleEffect; 10 | import static com.ray3k.gdxparticleeditor.Core.selectedEmitter; 11 | import static com.ray3k.gdxparticleeditor.widgets.panels.EffectEmittersPanel.effectEmittersPanel; 12 | import static com.ray3k.gdxparticleeditor.widgets.panels.EmitterPropertiesPanel.emitterPropertiesPanel; 13 | 14 | /** 15 | * Undoable to set the relative value of a ScaledNumericValue pair (ex. Size) 16 | */ 17 | @Data 18 | @AllArgsConstructor 19 | public class DualScaledNumericValueRelativeUndoable implements Undoable { 20 | private ParticleEmitter emitter; 21 | private ScaledNumericValue xValue; 22 | private ScaledNumericValue yValue; 23 | private boolean active; 24 | private String description; 25 | 26 | @Override 27 | public void undo() { 28 | selectedEmitter = emitter; 29 | 30 | xValue.setRelative(!active); 31 | yValue.setRelative(!active); 32 | refreshDisplay(); 33 | } 34 | 35 | @Override 36 | public void redo() { 37 | selectedEmitter = emitter; 38 | 39 | xValue.setRelative(active); 40 | yValue.setRelative(active); 41 | refreshDisplay(); 42 | } 43 | 44 | @Override 45 | public void start() { 46 | xValue.setRelative(active); 47 | yValue.setRelative(active); 48 | } 49 | 50 | @Override 51 | public String getDescription() { 52 | return description; 53 | } 54 | 55 | private void refreshDisplay() { 56 | effectEmittersPanel.populateEmitters(); 57 | emitterPropertiesPanel.populateScrollTable(null); 58 | particleEffect.reset(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/undo/undoables/DualScaledNumericValueUndoable.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.undo.undoables; 2 | 3 | import com.badlogic.gdx.graphics.g2d.ParticleEmitter; 4 | import com.badlogic.gdx.graphics.g2d.ParticleEmitter.ScaledNumericValue; 5 | import com.ray3k.gdxparticleeditor.undo.Undoable; 6 | import lombok.Builder; 7 | 8 | import static com.ray3k.gdxparticleeditor.Core.particleEffect; 9 | import static com.ray3k.gdxparticleeditor.Core.selectedEmitter; 10 | import static com.ray3k.gdxparticleeditor.widgets.panels.EffectEmittersPanel.effectEmittersPanel; 11 | import static com.ray3k.gdxparticleeditor.widgets.panels.EmitterPropertiesPanel.emitterPropertiesPanel; 12 | 13 | /** 14 | * Undoable to set the value of a ScaledNumericValue pair (ex. Size) 15 | */ 16 | @Builder(toBuilder = true) 17 | public class DualScaledNumericValueUndoable implements Undoable { 18 | private ParticleEmitter emitter; 19 | public final ScaledNumericValue newXvalue = new ScaledNumericValue(); 20 | public final ScaledNumericValue oldXvalue = new ScaledNumericValue(); 21 | private ScaledNumericValue xValue; 22 | public final ScaledNumericValue newYvalue = new ScaledNumericValue(); 23 | public final ScaledNumericValue oldYvalue = new ScaledNumericValue(); 24 | private ScaledNumericValue yValue; 25 | private String description; 26 | 27 | @Override 28 | public void undo() { 29 | selectedEmitter = emitter; 30 | 31 | xValue.set(oldXvalue); 32 | yValue.set(oldYvalue); 33 | refreshDisplay(); 34 | } 35 | 36 | @Override 37 | public void redo() { 38 | selectedEmitter = emitter; 39 | 40 | xValue.set(newXvalue); 41 | yValue.set(newYvalue); 42 | refreshDisplay(); 43 | } 44 | 45 | @Override 46 | public void start() { 47 | xValue.set(newXvalue); 48 | yValue.set(newYvalue); 49 | } 50 | 51 | @Override 52 | public String getDescription() { 53 | return description; 54 | } 55 | 56 | private void refreshDisplay() { 57 | effectEmittersPanel.populateEmitters(); 58 | emitterPropertiesPanel.populateScrollTable(null); 59 | particleEffect.reset(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/undo/undoables/ImagesAddUndoable.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.undo.undoables; 2 | 3 | import com.badlogic.gdx.files.FileHandle; 4 | import com.badlogic.gdx.graphics.Texture; 5 | import com.badlogic.gdx.graphics.g2d.ParticleEmitter; 6 | import com.badlogic.gdx.graphics.g2d.Sprite; 7 | import com.badlogic.gdx.utils.Array; 8 | import com.badlogic.gdx.utils.ObjectMap; 9 | import com.ray3k.gdxparticleeditor.Utils; 10 | import com.ray3k.gdxparticleeditor.undo.Undoable; 11 | import lombok.AllArgsConstructor; 12 | 13 | import static com.ray3k.gdxparticleeditor.Core.*; 14 | import static com.ray3k.gdxparticleeditor.widgets.panels.EffectEmittersPanel.effectEmittersPanel; 15 | import static com.ray3k.gdxparticleeditor.widgets.panels.EmitterPropertiesPanel.emitterPropertiesPanel; 16 | 17 | /** 18 | * Undoable to add images to the emitter. 19 | */ 20 | @AllArgsConstructor 21 | public class ImagesAddUndoable implements Undoable { 22 | private ParticleEmitter emitter; 23 | private int imageInsertIndex; 24 | private Array selectedFileHandles; 25 | private String description; 26 | private final Array newImagePaths = new Array<>(); 27 | private final ObjectMap newFileHandles = new ObjectMap<>(); 28 | private final ObjectMap newSpriteMap = new ObjectMap<>(); 29 | private final Array newSprites = new Array<>(); 30 | 31 | @Override 32 | public void undo() { 33 | selectedEmitter = emitter; 34 | 35 | emitter.getImagePaths().removeRange(imageInsertIndex, imageInsertIndex + newImagePaths.size - 1); 36 | Utils.removeUnusedImageFiles(); 37 | for (var newSprite : newSpriteMap) { 38 | sprites.remove(newSprite.key); 39 | } 40 | emitter.getSprites().removeRange(imageInsertIndex, imageInsertIndex + newSprites.size - 1); 41 | refreshDisplay(); 42 | } 43 | 44 | @Override 45 | public void redo() { 46 | selectedEmitter = emitter; 47 | 48 | for (int i = 0; i < newImagePaths.size; i++) { 49 | emitter.getImagePaths().insert(imageInsertIndex + i, newImagePaths.get(i)); 50 | } 51 | fileHandles.putAll(newFileHandles); 52 | sprites.putAll(newSpriteMap); 53 | for (int i = 0; i < newSprites.size; i++) { 54 | emitter.getSprites().insert(imageInsertIndex + i, newSprites.get(i)); 55 | } 56 | refreshDisplay(); 57 | } 58 | 59 | @Override 60 | public void start() { 61 | for (var fileHandle : selectedFileHandles) { 62 | var path = fileHandle.name(); 63 | newImagePaths.add(path); 64 | newFileHandles.put(path, fileHandle); 65 | var sprite = new Sprite(new Texture(fileHandle)); 66 | newSpriteMap.put(path, sprite); 67 | newSprites.add(sprite); 68 | } 69 | 70 | for (int i = 0; i < newImagePaths.size; i++) { 71 | emitter.getImagePaths().insert(imageInsertIndex + i, newImagePaths.get(i)); 72 | } 73 | fileHandles.putAll(newFileHandles); 74 | sprites.putAll(newSpriteMap); 75 | for (int i = 0; i < newSprites.size; i++) { 76 | emitter.getSprites().insert(imageInsertIndex + i, newSprites.get(i)); 77 | } 78 | } 79 | 80 | @Override 81 | public String getDescription() { 82 | return description; 83 | } 84 | 85 | private void refreshDisplay() { 86 | effectEmittersPanel.populateEmitters(); 87 | emitterPropertiesPanel.populateScrollTable(null); 88 | particleEffect.reset(); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/undo/undoables/ImagesMoveUndoable.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.undo.undoables; 2 | 3 | import com.badlogic.gdx.graphics.g2d.ParticleEmitter; 4 | import com.ray3k.gdxparticleeditor.undo.Undoable; 5 | import lombok.AllArgsConstructor; 6 | 7 | import static com.ray3k.gdxparticleeditor.Core.particleEffect; 8 | import static com.ray3k.gdxparticleeditor.Core.selectedEmitter; 9 | import static com.ray3k.gdxparticleeditor.widgets.panels.EffectEmittersPanel.effectEmittersPanel; 10 | import static com.ray3k.gdxparticleeditor.widgets.panels.EmitterPropertiesPanel.emitterPropertiesPanel; 11 | 12 | /** 13 | * Undoable to rearrange the images in the emitter. 14 | */ 15 | @AllArgsConstructor 16 | public class ImagesMoveUndoable implements Undoable { 17 | private ParticleEmitter emitter; 18 | private int oldIndex; 19 | private int newIndex; 20 | private String description; 21 | 22 | @Override 23 | public void undo() { 24 | selectedEmitter = emitter; 25 | 26 | var paths = emitter.getImagePaths(); 27 | var sprites = emitter.getSprites(); 28 | var path = paths.get(newIndex); 29 | var sprite = sprites.get(newIndex); 30 | paths.removeIndex(newIndex); 31 | sprites.removeIndex(newIndex); 32 | paths.insert(oldIndex, path); 33 | sprites.insert(oldIndex, sprite); 34 | refreshDisplay(); 35 | } 36 | 37 | @Override 38 | public void redo() { 39 | selectedEmitter = emitter; 40 | 41 | var paths = emitter.getImagePaths(); 42 | var sprites = emitter.getSprites(); 43 | var path = paths.get(oldIndex); 44 | var sprite = sprites.get(oldIndex); 45 | paths.removeIndex(oldIndex); 46 | sprites.removeIndex(oldIndex); 47 | paths.insert(newIndex, path); 48 | sprites.insert(newIndex, sprite); 49 | refreshDisplay(); 50 | } 51 | 52 | @Override 53 | public void start() { 54 | var paths = emitter.getImagePaths(); 55 | var sprites = emitter.getSprites(); 56 | var path = paths.get(oldIndex); 57 | var sprite = sprites.get(oldIndex); 58 | paths.removeIndex(oldIndex); 59 | sprites.removeIndex(oldIndex); 60 | paths.insert(newIndex, path); 61 | sprites.insert(newIndex, sprite); 62 | } 63 | 64 | @Override 65 | public String getDescription() { 66 | return description; 67 | } 68 | 69 | private void refreshDisplay() { 70 | effectEmittersPanel.populateEmitters(); 71 | emitterPropertiesPanel.populateScrollTable(null); 72 | particleEffect.reset(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/undo/undoables/ImagesRemoveUndoable.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.undo.undoables; 2 | 3 | import com.badlogic.gdx.files.FileHandle; 4 | import com.badlogic.gdx.graphics.g2d.ParticleEmitter; 5 | import com.badlogic.gdx.graphics.g2d.Sprite; 6 | import com.ray3k.gdxparticleeditor.Utils; 7 | import com.ray3k.gdxparticleeditor.undo.Undoable; 8 | 9 | import static com.ray3k.gdxparticleeditor.Core.*; 10 | import static com.ray3k.gdxparticleeditor.widgets.panels.EffectEmittersPanel.effectEmittersPanel; 11 | import static com.ray3k.gdxparticleeditor.widgets.panels.EmitterPropertiesPanel.emitterPropertiesPanel; 12 | 13 | /** 14 | * Undoable to remove images from the emitter. 15 | */ 16 | public class ImagesRemoveUndoable implements Undoable { 17 | private ParticleEmitter emitter; 18 | private String path; 19 | private FileHandle fileHandle; 20 | private Sprite sprite; 21 | private String description; 22 | private int index; 23 | 24 | public ImagesRemoveUndoable(ParticleEmitter emitter, String path, FileHandle fileHandle, Sprite sprite, 25 | String description) { 26 | this.emitter = emitter; 27 | this.path = path; 28 | this.fileHandle = fileHandle; 29 | this.sprite = sprite; 30 | this.description = description; 31 | index = emitter.getSprites().indexOf(sprite, true); 32 | } 33 | 34 | @Override 35 | public void undo() { 36 | selectedEmitter = emitter; 37 | 38 | emitter.getImagePaths().insert(index, path); 39 | fileHandles.put(path, fileHandle); 40 | sprites.put(path, sprite); 41 | emitter.getSprites().insert(index, sprite); 42 | refreshDisplay(); 43 | } 44 | 45 | @Override 46 | public void redo() { 47 | selectedEmitter = emitter; 48 | 49 | emitter.getImagePaths().removeValue(path, false); 50 | Utils.removeUnusedImageFiles(); 51 | sprites.remove(path); 52 | emitter.getSprites().removeValue(sprite, true); 53 | refreshDisplay(); 54 | } 55 | 56 | @Override 57 | public void start() { 58 | emitter.getImagePaths().removeValue(path, false); 59 | Utils.removeUnusedImageFiles(); 60 | sprites.remove(path); 61 | emitter.getSprites().removeValue(sprite, true); 62 | } 63 | 64 | @Override 65 | public String getDescription() { 66 | return description; 67 | } 68 | 69 | private void refreshDisplay() { 70 | effectEmittersPanel.populateEmitters(); 71 | emitterPropertiesPanel.populateScrollTable(null); 72 | particleEffect.reset(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/undo/undoables/ImagesSpriteModeUndoable.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.undo.undoables; 2 | 3 | import com.badlogic.gdx.graphics.g2d.ParticleEmitter; 4 | import com.badlogic.gdx.graphics.g2d.ParticleEmitter.SpriteMode; 5 | import com.ray3k.gdxparticleeditor.undo.Undoable; 6 | import lombok.AllArgsConstructor; 7 | 8 | import static com.ray3k.gdxparticleeditor.Core.particleEffect; 9 | import static com.ray3k.gdxparticleeditor.Core.selectedEmitter; 10 | import static com.ray3k.gdxparticleeditor.widgets.panels.EffectEmittersPanel.effectEmittersPanel; 11 | import static com.ray3k.gdxparticleeditor.widgets.panels.EmitterPropertiesPanel.emitterPropertiesPanel; 12 | 13 | /** 14 | * Undoable to change the sprite mode of the emitter. 15 | */ 16 | @AllArgsConstructor 17 | public class ImagesSpriteModeUndoable implements Undoable { 18 | private ParticleEmitter emitter; 19 | private SpriteMode spriteMode; 20 | private SpriteMode oldSpriteMode; 21 | private String description; 22 | 23 | @Override 24 | public void undo() { 25 | selectedEmitter = emitter; 26 | 27 | emitter.setSpriteMode(oldSpriteMode); 28 | refreshDisplay(); 29 | } 30 | 31 | @Override 32 | public void redo() { 33 | selectedEmitter = emitter; 34 | 35 | emitter.setSpriteMode(spriteMode); 36 | refreshDisplay(); 37 | } 38 | 39 | @Override 40 | public void start() { 41 | emitter.setSpriteMode(spriteMode); 42 | } 43 | 44 | @Override 45 | public String getDescription() { 46 | return description; 47 | } 48 | 49 | private void refreshDisplay() { 50 | effectEmittersPanel.populateEmitters(); 51 | emitterPropertiesPanel.populateScrollTable(null); 52 | particleEffect.reset(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/undo/undoables/MergeEmitterUndoable.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.undo.undoables; 2 | 3 | import com.badlogic.gdx.files.FileHandle; 4 | import com.badlogic.gdx.graphics.g2d.ParticleEmitter; 5 | import com.badlogic.gdx.graphics.g2d.Sprite; 6 | import com.badlogic.gdx.utils.ObjectMap; 7 | import com.badlogic.gdx.utils.OrderedMap; 8 | import com.ray3k.gdxparticleeditor.undo.Undoable; 9 | import lombok.Builder; 10 | 11 | import static com.ray3k.gdxparticleeditor.Core.*; 12 | import static com.ray3k.gdxparticleeditor.widgets.panels.EffectEmittersPanel.effectEmittersPanel; 13 | import static com.ray3k.gdxparticleeditor.widgets.panels.EmitterPropertiesPanel.emitterPropertiesPanel; 14 | 15 | /** 16 | * Undoable to merge a new list of emitters with the existing emitters. 17 | */ 18 | @Builder 19 | public class MergeEmitterUndoable implements Undoable { 20 | private OrderedMap oldActiveEmitters; 21 | private ObjectMap oldFileHandles; 22 | private ObjectMap oldSprites; 23 | private int oldSelectedIndex; 24 | private OrderedMap newActiveEmitters; 25 | private ObjectMap newFileHandles; 26 | private ObjectMap newSprites; 27 | private int newSelectedIndex; 28 | private String description; 29 | 30 | @Override 31 | public void undo() { 32 | particleEffect.getEmitters().clear(); 33 | 34 | activeEmitters.clear(); 35 | for (var oldActiveEmitter : oldActiveEmitters) { 36 | activeEmitters.put(oldActiveEmitter.key, oldActiveEmitter.value); 37 | if (oldActiveEmitter.value) particleEffect.getEmitters().add(oldActiveEmitter.key); 38 | } 39 | 40 | fileHandles.clear(); 41 | fileHandles.putAll(oldFileHandles); 42 | 43 | sprites.clear(); 44 | sprites.putAll(oldSprites); 45 | 46 | selectedEmitter = activeEmitters.orderedKeys().get(oldSelectedIndex); 47 | 48 | refreshDisplay(); 49 | } 50 | 51 | @Override 52 | public void redo() { 53 | particleEffect.getEmitters().clear(); 54 | 55 | activeEmitters.clear(); 56 | for (var newActiveEmitter : newActiveEmitters) { 57 | activeEmitters.put(newActiveEmitter.key, newActiveEmitter.value); 58 | if (newActiveEmitter.value) particleEffect.getEmitters().add(newActiveEmitter.key); 59 | } 60 | 61 | fileHandles.clear(); 62 | fileHandles.putAll(newFileHandles); 63 | 64 | sprites.clear(); 65 | sprites.putAll(newSprites); 66 | 67 | selectedEmitter = activeEmitters.orderedKeys().get(newSelectedIndex); 68 | 69 | refreshDisplay(); 70 | } 71 | 72 | @Override 73 | public void start() { 74 | selectedEmitter = activeEmitters.orderedKeys().get(newSelectedIndex); 75 | particleEffect.reset(); 76 | } 77 | 78 | @Override 79 | public String getDescription() { 80 | return description; 81 | } 82 | 83 | private void refreshDisplay() { 84 | effectEmittersPanel.populateEmitters(); 85 | effectEmittersPanel.updateDisableableWidgets(); 86 | emitterPropertiesPanel.populateScrollTable(null); 87 | particleEffect.reset(); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/undo/undoables/MoveEmitterUndoable.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.undo.undoables; 2 | 3 | import com.badlogic.gdx.graphics.g2d.ParticleEmitter; 4 | import com.ray3k.gdxparticleeditor.undo.Undoable; 5 | import lombok.AllArgsConstructor; 6 | 7 | import static com.ray3k.gdxparticleeditor.Core.*; 8 | import static com.ray3k.gdxparticleeditor.widgets.panels.EffectEmittersPanel.effectEmittersPanel; 9 | import static com.ray3k.gdxparticleeditor.widgets.panels.EmitterPropertiesPanel.emitterPropertiesPanel; 10 | 11 | /** 12 | * Undoable to rearrange the emitters in the particle effect. 13 | */ 14 | @AllArgsConstructor 15 | public class MoveEmitterUndoable implements Undoable { 16 | private ParticleEmitter emitter; 17 | private int oldIndex; 18 | private int newIndex; 19 | private String description; 20 | 21 | @Override 22 | public void undo() { 23 | activeEmitters.orderedKeys().removeIndex(newIndex); 24 | activeEmitters.orderedKeys().insert(oldIndex, emitter); 25 | 26 | particleEffect.getEmitters().clear(); 27 | for (var entry : activeEmitters.entries()) { 28 | if (entry.value) particleEffect.getEmitters().add(entry.key); 29 | } 30 | 31 | selectedEmitter = emitter; 32 | refreshDisplay(); 33 | } 34 | 35 | @Override 36 | public void redo() { 37 | activeEmitters.orderedKeys().removeIndex(oldIndex); 38 | activeEmitters.orderedKeys().insert(newIndex, emitter); 39 | 40 | particleEffect.getEmitters().clear(); 41 | for (var entry : activeEmitters.entries()) { 42 | if (entry.value) particleEffect.getEmitters().add(entry.key); 43 | } 44 | 45 | selectedEmitter = emitter; 46 | refreshDisplay(); 47 | } 48 | 49 | @Override 50 | public void start() { 51 | activeEmitters.orderedKeys().removeIndex(oldIndex); 52 | activeEmitters.orderedKeys().insert(newIndex, emitter); 53 | 54 | particleEffect.getEmitters().clear(); 55 | for (var entry : activeEmitters.entries()) { 56 | if (entry.value) particleEffect.getEmitters().add(entry.key); 57 | } 58 | 59 | selectedEmitter = emitter; 60 | } 61 | 62 | @Override 63 | public String getDescription() { 64 | return description; 65 | } 66 | 67 | private void refreshDisplay() { 68 | effectEmittersPanel.populateEmitters(); 69 | emitterPropertiesPanel.populateScrollTable(null); 70 | particleEffect.reset(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/undo/undoables/NewEmitterUndoable.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.undo.undoables; 2 | 3 | import com.badlogic.gdx.graphics.g2d.ParticleEmitter; 4 | import com.ray3k.gdxparticleeditor.undo.Undoable; 5 | import lombok.AllArgsConstructor; 6 | 7 | import static com.ray3k.gdxparticleeditor.Core.*; 8 | import static com.ray3k.gdxparticleeditor.widgets.panels.EffectEmittersPanel.effectEmittersPanel; 9 | import static com.ray3k.gdxparticleeditor.widgets.panels.EmitterPropertiesPanel.emitterPropertiesPanel; 10 | 11 | /** 12 | * Undoable to create a new emitter and add it to the particle effect. 13 | */ 14 | @AllArgsConstructor 15 | public class NewEmitterUndoable implements Undoable { 16 | private ParticleEmitter emitter; 17 | private String description; 18 | 19 | @Override 20 | public void undo() { 21 | var index = particleEffect.getEmitters().indexOf(emitter, true); 22 | particleEffect.getEmitters().removeIndex(index); 23 | activeEmitters.remove(emitter); 24 | selectedEmitter = particleEffect.getEmitters().get(Math.min(index, activeEmitters.orderedKeys().size - 1)); 25 | refreshDisplay(); 26 | } 27 | 28 | @Override 29 | public void redo() { 30 | particleEffect.getEmitters().add(emitter); 31 | activeEmitters.put(emitter, true); 32 | selectedEmitter = emitter; 33 | refreshDisplay(); 34 | } 35 | 36 | @Override 37 | public void start() { 38 | particleEffect.getEmitters().add(emitter); 39 | activeEmitters.put(emitter, true); 40 | selectedEmitter = emitter; 41 | } 42 | 43 | @Override 44 | public String getDescription() { 45 | return description; 46 | } 47 | 48 | private void refreshDisplay() { 49 | effectEmittersPanel.populateEmitters(); 50 | effectEmittersPanel.updateDisableableWidgets(); 51 | emitterPropertiesPanel.populateScrollTable(null); 52 | particleEffect.reset(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/undo/undoables/OptionsUndoable.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.undo.undoables; 2 | 3 | import com.badlogic.gdx.graphics.g2d.ParticleEmitter; 4 | import com.ray3k.gdxparticleeditor.undo.Undoable; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | 8 | import static com.ray3k.gdxparticleeditor.Core.particleEffect; 9 | import static com.ray3k.gdxparticleeditor.widgets.panels.EmitterPropertiesPanel.emitterPropertiesPanel; 10 | 11 | /** 12 | * Undoable to change the options of the particle effect. 13 | */ 14 | @Data 15 | @AllArgsConstructor 16 | public class OptionsUndoable implements Undoable { 17 | private ParticleEmitter emitter; 18 | public enum Type { 19 | ADDITIVE, ATTACHED, CONTINUOUS, ALIGNED, BEHIND, PMA 20 | } 21 | private Type type; 22 | private boolean newValue; 23 | private String description; 24 | 25 | @Override 26 | public void undo() { 27 | setOption(!newValue); 28 | refreshDisplay(); 29 | } 30 | 31 | @Override 32 | public void redo() { 33 | setOption(newValue); 34 | refreshDisplay(); 35 | } 36 | 37 | private void setOption(boolean value) { 38 | switch (type) { 39 | case ADDITIVE: 40 | emitter.setAdditive(value); 41 | break; 42 | case ATTACHED: 43 | emitter.setAttached(value); 44 | break; 45 | case CONTINUOUS: 46 | emitter.setContinuous(value); 47 | break; 48 | case ALIGNED: 49 | emitter.setAligned(value); 50 | break; 51 | case BEHIND: 52 | emitter.setBehind(value); 53 | break; 54 | case PMA: 55 | emitter.setPremultipliedAlpha(value); 56 | break; 57 | } 58 | } 59 | 60 | @Override 61 | public void start() { 62 | setOption(newValue); 63 | } 64 | 65 | @Override 66 | public String getDescription() { 67 | return description; 68 | } 69 | 70 | private void refreshDisplay() { 71 | emitterPropertiesPanel.populateScrollTable(null); 72 | particleEffect.reset(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/undo/undoables/RangedNumericValueUndoable.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.undo.undoables; 2 | 3 | import com.badlogic.gdx.graphics.g2d.ParticleEmitter; 4 | import com.badlogic.gdx.graphics.g2d.ParticleEmitter.RangedNumericValue; 5 | import com.ray3k.gdxparticleeditor.undo.Undoable; 6 | import lombok.AllArgsConstructor; 7 | 8 | import static com.ray3k.gdxparticleeditor.Core.particleEffect; 9 | import static com.ray3k.gdxparticleeditor.Core.selectedEmitter; 10 | import static com.ray3k.gdxparticleeditor.widgets.panels.EffectEmittersPanel.effectEmittersPanel; 11 | import static com.ray3k.gdxparticleeditor.widgets.panels.EmitterPropertiesPanel.emitterPropertiesPanel; 12 | 13 | /** 14 | * Undoable to change the value of a RangedNumericValue. 15 | */ 16 | @AllArgsConstructor 17 | public class RangedNumericValueUndoable implements Undoable { 18 | private ParticleEmitter emitter; 19 | public final RangedNumericValue newValue = new RangedNumericValue(); 20 | public final RangedNumericValue oldValue = new RangedNumericValue(); 21 | private RangedNumericValue value; 22 | private String description; 23 | 24 | @Override 25 | public void undo() { 26 | selectedEmitter = emitter; 27 | 28 | value.set(oldValue); 29 | refreshDisplay(); 30 | } 31 | 32 | @Override 33 | public void redo() { 34 | selectedEmitter = emitter; 35 | 36 | value.set(newValue); 37 | refreshDisplay(); 38 | } 39 | 40 | @Override 41 | public void start() { 42 | value.set(newValue); 43 | } 44 | 45 | @Override 46 | public String getDescription() { 47 | return description; 48 | } 49 | 50 | private void refreshDisplay() { 51 | effectEmittersPanel.populateEmitters(); 52 | emitterPropertiesPanel.populateScrollTable(null); 53 | particleEffect.reset(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/undo/undoables/RenameEmitterUndoable.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.undo.undoables; 2 | 3 | import com.badlogic.gdx.graphics.g2d.ParticleEmitter; 4 | import com.ray3k.gdxparticleeditor.undo.Undoable; 5 | import lombok.AllArgsConstructor; 6 | 7 | import static com.ray3k.gdxparticleeditor.Core.particleEffect; 8 | import static com.ray3k.gdxparticleeditor.widgets.panels.EffectEmittersPanel.effectEmittersPanel; 9 | import static com.ray3k.gdxparticleeditor.widgets.panels.EmitterPropertiesPanel.emitterPropertiesPanel; 10 | 11 | /** 12 | * Undoable to change the name of an emitter. 13 | */ 14 | @AllArgsConstructor 15 | public class RenameEmitterUndoable implements Undoable { 16 | private ParticleEmitter emitter; 17 | private String oldName; 18 | private String newName; 19 | private String description; 20 | 21 | @Override 22 | public void undo() { 23 | emitter.setName(oldName); 24 | refreshDisplay(); 25 | } 26 | 27 | @Override 28 | public void redo() { 29 | emitter.setName(newName); 30 | refreshDisplay(); 31 | } 32 | 33 | @Override 34 | public void start() { 35 | emitter.setName(newName); 36 | } 37 | 38 | @Override 39 | public String getDescription() { 40 | return description; 41 | } 42 | 43 | private void refreshDisplay() { 44 | effectEmittersPanel.populateEmitters(); 45 | effectEmittersPanel.updateDisableableWidgets(); 46 | emitterPropertiesPanel.populateScrollTable(null); 47 | particleEffect.reset(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/undo/undoables/ScaledNumericValueIndependentUndoable.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.undo.undoables; 2 | 3 | import com.badlogic.gdx.graphics.g2d.ParticleEmitter; 4 | import com.badlogic.gdx.graphics.g2d.ParticleEmitter.IndependentScaledNumericValue; 5 | import com.ray3k.gdxparticleeditor.undo.Undoable; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Data; 8 | 9 | import static com.ray3k.gdxparticleeditor.Core.particleEffect; 10 | import static com.ray3k.gdxparticleeditor.Core.selectedEmitter; 11 | import static com.ray3k.gdxparticleeditor.widgets.panels.EffectEmittersPanel.effectEmittersPanel; 12 | import static com.ray3k.gdxparticleeditor.widgets.panels.EmitterPropertiesPanel.emitterPropertiesPanel; 13 | 14 | /** 15 | * Undoable to change the independent value of a scaled numeric value. 16 | */ 17 | @Data 18 | @AllArgsConstructor 19 | public class ScaledNumericValueIndependentUndoable implements Undoable { 20 | private ParticleEmitter emitter; 21 | private IndependentScaledNumericValue value; 22 | private boolean active; 23 | private String description; 24 | 25 | @Override 26 | public void undo() { 27 | selectedEmitter = emitter; 28 | 29 | value.setIndependent(!active); 30 | refreshDisplay(); 31 | } 32 | 33 | @Override 34 | public void redo() { 35 | selectedEmitter = emitter; 36 | 37 | value.setIndependent(active); 38 | refreshDisplay(); 39 | } 40 | 41 | @Override 42 | public void start() { 43 | value.setIndependent(active); 44 | } 45 | 46 | @Override 47 | public String getDescription() { 48 | return description; 49 | } 50 | 51 | private void refreshDisplay() { 52 | effectEmittersPanel.populateEmitters(); 53 | emitterPropertiesPanel.populateScrollTable(null); 54 | particleEffect.reset(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/undo/undoables/ScaledNumericValueRelativeUndoable.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.undo.undoables; 2 | 3 | import com.badlogic.gdx.graphics.g2d.ParticleEmitter; 4 | import com.badlogic.gdx.graphics.g2d.ParticleEmitter.ScaledNumericValue; 5 | import com.ray3k.gdxparticleeditor.undo.Undoable; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Data; 8 | 9 | import static com.ray3k.gdxparticleeditor.Core.particleEffect; 10 | import static com.ray3k.gdxparticleeditor.Core.selectedEmitter; 11 | import static com.ray3k.gdxparticleeditor.widgets.panels.EffectEmittersPanel.effectEmittersPanel; 12 | import static com.ray3k.gdxparticleeditor.widgets.panels.EmitterPropertiesPanel.emitterPropertiesPanel; 13 | 14 | /** 15 | * Change the relative value of a scaled numeric value. 16 | */ 17 | @Data 18 | @AllArgsConstructor 19 | public class ScaledNumericValueRelativeUndoable implements Undoable { 20 | private ParticleEmitter emitter; 21 | private ScaledNumericValue value; 22 | private boolean active; 23 | private String description; 24 | 25 | @Override 26 | public void undo() { 27 | selectedEmitter = emitter; 28 | 29 | value.setRelative(!active); 30 | refreshDisplay(); 31 | } 32 | 33 | @Override 34 | public void redo() { 35 | selectedEmitter = emitter; 36 | 37 | value.setRelative(active); 38 | refreshDisplay(); 39 | } 40 | 41 | @Override 42 | public void start() { 43 | value.setRelative(active); 44 | } 45 | 46 | @Override 47 | public String getDescription() { 48 | return description; 49 | } 50 | 51 | private void refreshDisplay() { 52 | effectEmittersPanel.populateEmitters(); 53 | emitterPropertiesPanel.populateScrollTable(null); 54 | particleEffect.reset(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/undo/undoables/ScaledNumericValueUndoable.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.undo.undoables; 2 | 3 | import com.badlogic.gdx.graphics.g2d.ParticleEmitter; 4 | import com.badlogic.gdx.graphics.g2d.ParticleEmitter.ScaledNumericValue; 5 | import com.ray3k.gdxparticleeditor.undo.Undoable; 6 | import lombok.AllArgsConstructor; 7 | 8 | import static com.ray3k.gdxparticleeditor.Core.particleEffect; 9 | import static com.ray3k.gdxparticleeditor.Core.selectedEmitter; 10 | import static com.ray3k.gdxparticleeditor.widgets.panels.EffectEmittersPanel.effectEmittersPanel; 11 | import static com.ray3k.gdxparticleeditor.widgets.panels.EmitterPropertiesPanel.emitterPropertiesPanel; 12 | 13 | /** 14 | * Undoable to change the value of a scaled numeric value. 15 | */ 16 | @AllArgsConstructor 17 | public class ScaledNumericValueUndoable implements Undoable { 18 | private ParticleEmitter emitter; 19 | public final ScaledNumericValue newValue = new ScaledNumericValue(); 20 | public final ScaledNumericValue oldValue = new ScaledNumericValue(); 21 | private ScaledNumericValue value; 22 | private String description; 23 | 24 | @Override 25 | public void undo() { 26 | selectedEmitter = emitter; 27 | 28 | value.set(oldValue); 29 | refreshDisplay(); 30 | } 31 | 32 | @Override 33 | public void redo() { 34 | selectedEmitter = emitter; 35 | 36 | value.set(newValue); 37 | refreshDisplay(); 38 | } 39 | 40 | @Override 41 | public void start() { 42 | value.set(newValue); 43 | } 44 | 45 | @Override 46 | public String getDescription() { 47 | return description; 48 | } 49 | 50 | private void refreshDisplay() { 51 | effectEmittersPanel.populateEmitters(); 52 | emitterPropertiesPanel.populateScrollTable(null); 53 | particleEffect.reset(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/undo/undoables/SetPropertyUndoable.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.undo.undoables; 2 | 3 | import com.badlogic.gdx.graphics.g2d.ParticleEmitter; 4 | import com.badlogic.gdx.graphics.g2d.ParticleEmitter.ScaledNumericValue; 5 | import com.ray3k.gdxparticleeditor.undo.Undoable; 6 | import lombok.AllArgsConstructor; 7 | 8 | import static com.ray3k.gdxparticleeditor.Core.*; 9 | import static com.ray3k.gdxparticleeditor.widgets.panels.EffectEmittersPanel.effectEmittersPanel; 10 | import static com.ray3k.gdxparticleeditor.widgets.panels.EmitterPropertiesPanel.ShownProperty; 11 | import static com.ray3k.gdxparticleeditor.widgets.panels.EmitterPropertiesPanel.emitterPropertiesPanel; 12 | 13 | /** 14 | * Undoable to change the active/disabled status of an emitter. 15 | */ 16 | @AllArgsConstructor 17 | public class SetPropertyUndoable implements Undoable { 18 | private ParticleEmitter emitter; 19 | private ShownProperty property; 20 | private boolean active; 21 | private String description; 22 | 23 | @Override 24 | public void undo() { 25 | selectedEmitter = emitter; 26 | 27 | activateProperty(!active); 28 | 29 | refreshDisplay(!active); 30 | } 31 | 32 | @Override 33 | public void redo() { 34 | selectedEmitter = emitter; 35 | 36 | activateProperty(active); 37 | 38 | refreshDisplay(active); 39 | } 40 | 41 | @Override 42 | public void start() { 43 | activateProperty(active); 44 | 45 | if (active) emitterPropertiesPanel.populateScrollTable(property); 46 | else emitterPropertiesPanel.removeProperty(property); 47 | } 48 | 49 | public void activateProperty(boolean active) { 50 | switch (property) { 51 | case DELAY: 52 | selectedEmitter.getDelay().setActive(active); 53 | break; 54 | case LIFE_OFFSET: 55 | selectedEmitter.getLifeOffset().setActive(active); 56 | break; 57 | case X_OFFSET: 58 | selectedEmitter.getXOffsetValue().setActive(active); 59 | break; 60 | case Y_OFFSET: 61 | selectedEmitter.getYOffsetValue().setActive(active); 62 | break; 63 | case VELOCITY: 64 | selectedEmitter.getVelocity().setActive(active); 65 | break; 66 | case ANGLE: 67 | selectedEmitter.getAngle().setActive(active); 68 | if (!active) { 69 | var value = new ScaledNumericValue(); 70 | value.set(selectedEmitter.getAngle()); 71 | defaultAngleMap.put(selectedEmitter, value); 72 | 73 | selectedEmitter.getAngle().setHigh(0); 74 | selectedEmitter.getAngle().setLow(0); 75 | } else { 76 | selectedEmitter.getAngle().set(defaultAngleMap.get(selectedEmitter)); 77 | } 78 | break; 79 | case ROTATION: 80 | selectedEmitter.getRotation().setActive(active); 81 | break; 82 | case WIND: 83 | selectedEmitter.getWind().setActive(active); 84 | break; 85 | case GRAVITY: 86 | selectedEmitter.getGravity().setActive(active); 87 | break; 88 | } 89 | } 90 | 91 | @Override 92 | public String getDescription() { 93 | return description; 94 | } 95 | 96 | private void refreshDisplay(boolean active) { 97 | effectEmittersPanel.populateEmitters(); 98 | if (active) emitterPropertiesPanel.populateScrollTable(property); 99 | else emitterPropertiesPanel.removeProperty(property); 100 | particleEffect.reset(); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/undo/undoables/SpawnEdgesUndoable.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.undo.undoables; 2 | 3 | import com.badlogic.gdx.graphics.g2d.ParticleEmitter; 4 | import com.badlogic.gdx.graphics.g2d.ParticleEmitter.SpawnShapeValue; 5 | import com.ray3k.gdxparticleeditor.undo.Undoable; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Data; 8 | 9 | import static com.ray3k.gdxparticleeditor.Core.particleEffect; 10 | import static com.ray3k.gdxparticleeditor.Core.selectedEmitter; 11 | import static com.ray3k.gdxparticleeditor.widgets.panels.EffectEmittersPanel.effectEmittersPanel; 12 | import static com.ray3k.gdxparticleeditor.widgets.panels.EmitterPropertiesPanel.emitterPropertiesPanel; 13 | 14 | /** 15 | * Undoable to change the edges value of a spawn shape. 16 | */ 17 | @Data 18 | @AllArgsConstructor 19 | public class SpawnEdgesUndoable implements Undoable { 20 | private ParticleEmitter emitter; 21 | private SpawnShapeValue value; 22 | private boolean active; 23 | private String description; 24 | 25 | @Override 26 | public void undo() { 27 | selectedEmitter = emitter; 28 | 29 | value.setEdges(!active); 30 | refreshDisplay(); 31 | } 32 | 33 | @Override 34 | public void redo() { 35 | selectedEmitter = emitter; 36 | 37 | value.setEdges(active); 38 | refreshDisplay(); 39 | } 40 | 41 | @Override 42 | public void start() { 43 | value.setEdges(active); 44 | } 45 | 46 | @Override 47 | public String getDescription() { 48 | return description; 49 | } 50 | 51 | private void refreshDisplay() { 52 | effectEmittersPanel.populateEmitters(); 53 | emitterPropertiesPanel.populateScrollTable(null); 54 | particleEffect.reset(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/undo/undoables/SpawnSideUndoable.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.undo.undoables; 2 | 3 | import com.badlogic.gdx.graphics.g2d.ParticleEmitter; 4 | import com.badlogic.gdx.graphics.g2d.ParticleEmitter.SpawnEllipseSide; 5 | import com.badlogic.gdx.graphics.g2d.ParticleEmitter.SpawnShapeValue; 6 | import com.ray3k.gdxparticleeditor.undo.Undoable; 7 | import lombok.AllArgsConstructor; 8 | import lombok.Data; 9 | 10 | import static com.ray3k.gdxparticleeditor.Core.particleEffect; 11 | import static com.ray3k.gdxparticleeditor.Core.selectedEmitter; 12 | import static com.ray3k.gdxparticleeditor.widgets.panels.EffectEmittersPanel.effectEmittersPanel; 13 | import static com.ray3k.gdxparticleeditor.widgets.panels.EmitterPropertiesPanel.emitterPropertiesPanel; 14 | 15 | /** 16 | * Undoable to change the side of a spawn shape. 17 | */ 18 | @Data 19 | @AllArgsConstructor 20 | public class SpawnSideUndoable implements Undoable { 21 | private ParticleEmitter emitter; 22 | private SpawnShapeValue value; 23 | private SpawnEllipseSide side; 24 | private SpawnEllipseSide oldSide; 25 | private String description; 26 | 27 | @Override 28 | public void undo() { 29 | selectedEmitter = emitter; 30 | 31 | value.setSide(oldSide); 32 | refreshDisplay(); 33 | } 34 | 35 | @Override 36 | public void redo() { 37 | selectedEmitter = emitter; 38 | 39 | value.setSide(side); 40 | refreshDisplay(); 41 | } 42 | 43 | @Override 44 | public void start() { 45 | value.setSide(side); 46 | } 47 | 48 | @Override 49 | public String getDescription() { 50 | return description; 51 | } 52 | 53 | private void refreshDisplay() { 54 | effectEmittersPanel.populateEmitters(); 55 | emitterPropertiesPanel.populateScrollTable(null); 56 | particleEffect.reset(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/undo/undoables/SpawnTypeUndoable.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.undo.undoables; 2 | 3 | import com.badlogic.gdx.graphics.g2d.ParticleEmitter; 4 | import com.badlogic.gdx.graphics.g2d.ParticleEmitter.SpawnShapeValue; 5 | import com.ray3k.gdxparticleeditor.undo.Undoable; 6 | import com.ray3k.gdxparticleeditor.widgets.subpanels.SpawnSubPanel.SpawnType; 7 | import lombok.AllArgsConstructor; 8 | import lombok.Data; 9 | 10 | import static com.ray3k.gdxparticleeditor.Core.particleEffect; 11 | import static com.ray3k.gdxparticleeditor.Core.selectedEmitter; 12 | import static com.ray3k.gdxparticleeditor.widgets.panels.EffectEmittersPanel.effectEmittersPanel; 13 | import static com.ray3k.gdxparticleeditor.widgets.panels.EmitterPropertiesPanel.emitterPropertiesPanel; 14 | 15 | /** 16 | * Undoable to change the type of a spawn shape. 17 | */ 18 | @Data 19 | @AllArgsConstructor 20 | public class SpawnTypeUndoable implements Undoable { 21 | private ParticleEmitter emitter; 22 | private SpawnShapeValue value; 23 | private SpawnType spawnType; 24 | private SpawnType spawnTypeOld; 25 | private String description; 26 | 27 | @Override 28 | public void undo() { 29 | selectedEmitter = emitter; 30 | 31 | value.setShape(spawnTypeOld.spawnShape); 32 | refreshDisplay(); 33 | } 34 | 35 | @Override 36 | public void redo() { 37 | selectedEmitter = emitter; 38 | 39 | value.setShape(spawnType.spawnShape); 40 | refreshDisplay(); 41 | } 42 | 43 | @Override 44 | public void start() { 45 | value.setShape(spawnType.spawnShape); 46 | } 47 | 48 | public void refreshDisplay() { 49 | effectEmittersPanel.populateEmitters(); 50 | emitterPropertiesPanel.populateScrollTable(null); 51 | particleEffect.reset(); 52 | } 53 | 54 | @Override 55 | public String getDescription() { 56 | return description; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/undo/undoables/TintUndoable.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.undo.undoables; 2 | 3 | import com.badlogic.gdx.graphics.g2d.ParticleEmitter; 4 | import com.badlogic.gdx.graphics.g2d.ParticleEmitter.GradientColorValue; 5 | import com.ray3k.gdxparticleeditor.undo.Undoable; 6 | import com.ray3k.gdxparticleeditor.widgets.ColorGraph; 7 | import lombok.AllArgsConstructor; 8 | import lombok.Data; 9 | 10 | import static com.ray3k.gdxparticleeditor.Core.particleEffect; 11 | import static com.ray3k.gdxparticleeditor.Core.selectedEmitter; 12 | import static com.ray3k.gdxparticleeditor.widgets.panels.EffectEmittersPanel.effectEmittersPanel; 13 | import static com.ray3k.gdxparticleeditor.widgets.panels.EmitterPropertiesPanel.emitterPropertiesPanel; 14 | 15 | /** 16 | * Undoable to change the tint of a gradient color value. 17 | */ 18 | @Data 19 | @AllArgsConstructor 20 | public class TintUndoable implements Undoable { 21 | private ParticleEmitter emitter; 22 | private GradientColorValue value; 23 | private final GradientColorValue oldValue = new GradientColorValue(); 24 | private final GradientColorValue newValue = new GradientColorValue(); 25 | private ColorGraph colorGraph; 26 | private String description; 27 | 28 | @Override 29 | public void undo() { 30 | selectedEmitter = emitter; 31 | 32 | value.setTimeline(oldValue.getTimeline()); 33 | value.setColors(oldValue.getColors()); 34 | refreshDisplay(); 35 | } 36 | 37 | @Override 38 | public void redo() { 39 | selectedEmitter = emitter; 40 | 41 | value.setTimeline(newValue.getTimeline()); 42 | value.setColors(newValue.getColors()); 43 | refreshDisplay(); 44 | } 45 | 46 | @Override 47 | public void start() { 48 | value.setTimeline(newValue.getTimeline()); 49 | value.setColors(newValue.getColors()); 50 | } 51 | 52 | @Override 53 | public String getDescription() { 54 | return description; 55 | } 56 | 57 | private void refreshDisplay() { 58 | effectEmittersPanel.populateEmitters(); 59 | emitterPropertiesPanel.populateScrollTable(null); 60 | particleEffect.reset(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/widgets/CardGroup.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.widgets; 2 | 3 | import com.badlogic.gdx.scenes.scene2d.Actor; 4 | import com.badlogic.gdx.scenes.scene2d.ui.Container; 5 | import com.badlogic.gdx.utils.Array; 6 | 7 | /** 8 | * Widget that allows the user to flip through its children, displaying only one at a time. 9 | */ 10 | public class CardGroup extends Container { 11 | public final Array actors = new Array<>(); 12 | private int shownIndex = -1; 13 | 14 | public CardGroup(Actor... actors) { 15 | if (actors.length > 0) { 16 | this.actors.addAll(actors); 17 | showIndex(0); 18 | } 19 | fill(); 20 | } 21 | 22 | public void showIndex(int index) { 23 | shownIndex = index; 24 | super.setActor(actors.get(index)); 25 | } 26 | 27 | public Actor getActor(int index) { 28 | return actors.get(index); 29 | } 30 | 31 | public int getShownIndex() { 32 | return shownIndex; 33 | } 34 | 35 | public Actor getShownActor() { 36 | return actors.get(shownIndex); 37 | } 38 | 39 | public void add(Actor actor) { 40 | actors.add(actor); 41 | if (shownIndex == -1) showIndex(0); 42 | } 43 | 44 | public void remove(Actor actor) { 45 | 46 | } 47 | 48 | @Override @Deprecated 49 | public void setActor(Actor actor) { 50 | super.setActor(actor); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/widgets/EditableLabel.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.widgets; 2 | 3 | import com.badlogic.gdx.scenes.scene2d.Actor; 4 | import com.badlogic.gdx.scenes.scene2d.InputEvent; 5 | import com.badlogic.gdx.scenes.scene2d.Stage; 6 | import com.badlogic.gdx.scenes.scene2d.Touchable; 7 | import com.badlogic.gdx.scenes.scene2d.ui.Label; 8 | import com.badlogic.gdx.scenes.scene2d.ui.Label.LabelStyle; 9 | import com.badlogic.gdx.scenes.scene2d.ui.TextField; 10 | import com.badlogic.gdx.scenes.scene2d.ui.TextField.TextFieldStyle; 11 | import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; 12 | import com.badlogic.gdx.scenes.scene2d.utils.ClickListener; 13 | import com.badlogic.gdx.scenes.scene2d.utils.FocusListener; 14 | import lombok.Getter; 15 | 16 | /** 17 | * A label that shows ellipses when its size is decreased below its preferred dimensions. If the user clicks the label, 18 | * the label is converted to a textfield, allowing the text to be modified. 19 | */ 20 | public class EditableLabel extends ToggleGroup { 21 | @Getter 22 | private String text; 23 | public Label label; 24 | public TextField textField; 25 | public EditableLabelStyle style; 26 | 27 | public EditableLabel(String text, EditableLabelStyle style) { 28 | this.text = text; 29 | this.style = style; 30 | 31 | label = new Label(text, style.labelStyle); 32 | label.setEllipsis("..."); 33 | label.setTouchable(Touchable.enabled); 34 | table1.add(label).grow().minWidth(0); 35 | label.addListener(new ClickListener() { 36 | @Override 37 | public void clicked(InputEvent event, float x, float y) { 38 | swap(); 39 | getStage().setKeyboardFocus(textField); 40 | textField.selectAll(); 41 | } 42 | }); 43 | 44 | textField = new TextField(text, style.textFieldStyle); 45 | table2.add(textField).grow().minWidth(0); 46 | textField.addListener(new ChangeListener() { 47 | @Override 48 | public void changed(ChangeEvent event, Actor actor) { 49 | label.setText(textField.getText()); 50 | EditableLabel.this.text = textField.getText(); 51 | } 52 | }); 53 | textField.addListener(new FocusListener() { 54 | @Override 55 | public void keyboardFocusChanged(FocusEvent event, Actor actor, boolean focused) { 56 | if (focused) textField.selectAll(); 57 | else swap(); 58 | } 59 | }); 60 | } 61 | 62 | @Override 63 | protected void setStage(Stage stage) { 64 | super.setStage(stage); 65 | } 66 | 67 | public void setText(String text) { 68 | this.text = text; 69 | label.setText(text); 70 | textField.setText(text); 71 | } 72 | 73 | public void unfocused() { 74 | 75 | } 76 | 77 | public static class EditableLabelStyle { 78 | public LabelStyle labelStyle; 79 | public TextFieldStyle textFieldStyle; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/widgets/NoCaptureKeyboardFocusListener.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.widgets; 2 | 3 | import com.badlogic.gdx.scenes.scene2d.Event; 4 | import com.badlogic.gdx.scenes.scene2d.EventListener; 5 | 6 | /** 7 | * A hack to signify that the widget this listener is attached to should not trigger the keyboard focus changed event 8 | * in GDX Particle Editor. 9 | */ 10 | public class NoCaptureKeyboardFocusListener implements EventListener { 11 | @Override 12 | public boolean handle(Event event) { 13 | return false; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/widgets/Panel.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.widgets; 2 | 3 | import com.badlogic.gdx.scenes.scene2d.ui.Table; 4 | 5 | import static com.ray3k.gdxparticleeditor.Core.skin; 6 | 7 | /** 8 | * A layout widget comprised of three components: tabTable, topTable, and bodyTable. These are used in conjunction by 9 | * dependent classes to create a visual effect of a tabbed panel. 10 | */ 11 | public class Panel extends Table { 12 | public Table tabTable; 13 | public Table topTable; 14 | public Table bodyTable; 15 | 16 | public Panel() { 17 | tabTable = new Table(); 18 | add(tabTable); 19 | tabTable.setBackground(skin.getDrawable("panel-tab-10")); 20 | 21 | topTable = new Table(); 22 | add(topTable).growX(); 23 | topTable.setBackground(skin.getDrawable("panel-top-10")); 24 | 25 | row(); 26 | bodyTable = new Table(); 27 | add(bodyTable).grow().colspan(2); 28 | bodyTable.setBackground(skin.getDrawable("panel-body-10")); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/widgets/Toast.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.widgets; 2 | 3 | import com.badlogic.gdx.math.Interpolation; 4 | import com.badlogic.gdx.scenes.scene2d.Stage; 5 | import com.badlogic.gdx.scenes.scene2d.ui.Skin; 6 | import com.badlogic.gdx.scenes.scene2d.ui.Window.WindowStyle; 7 | import com.ray3k.stripe.PopTable; 8 | 9 | import static com.badlogic.gdx.scenes.scene2d.actions.Actions.*; 10 | 11 | public class Toast extends PopTable { 12 | private float transitionTime; 13 | private float showTime; 14 | 15 | public Toast(float transitionTime, float showTime) { 16 | this.transitionTime = transitionTime; 17 | this.showTime = showTime; 18 | } 19 | 20 | public Toast(Skin skin, float transitionTime, float showTime) { 21 | super(skin); 22 | this.transitionTime = transitionTime; 23 | this.showTime = showTime; 24 | } 25 | 26 | public Toast(Skin skin, String style, float transitionTime, float showTime) { 27 | super(skin, style); 28 | this.transitionTime = transitionTime; 29 | this.showTime = showTime; 30 | } 31 | 32 | public Toast(WindowStyle style, float transitionTime, float showTime) { 33 | super(style); 34 | this.transitionTime = transitionTime; 35 | this.showTime = showTime; 36 | } 37 | 38 | public Toast(PopTableStyle style, float transitionTime, float showTime) { 39 | super(style); 40 | this.transitionTime = transitionTime; 41 | this.showTime = showTime; 42 | } 43 | 44 | @Override 45 | public void show(Stage stage) { 46 | super.show(stage, null); 47 | setKeepSizedWithinStage(false); 48 | getParentGroup().setColor(1, 1, 1, 1); 49 | addAction(sequence( 50 | moveTo(getStage().getWidth() / 2f - getWidth() / 2f, -getHeight()), 51 | moveTo(getStage().getWidth() / 2f - getWidth() / 2f, 0, transitionTime, Interpolation.exp5Out), 52 | delay(showTime), 53 | run(this::hide) 54 | )); 55 | } 56 | 57 | @Override 58 | public void hide() { 59 | addAction(sequence( 60 | moveTo(getStage().getWidth() / 2f - getWidth() / 2f, -getHeight(), transitionTime, Interpolation.exp5In), 61 | run(() -> super.hide(null)) 62 | )); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/widgets/ToggleGroup.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.widgets; 2 | 3 | import com.badlogic.gdx.scenes.scene2d.ui.Container; 4 | import com.badlogic.gdx.scenes.scene2d.ui.Table; 5 | 6 | /** 7 | * A layout widget that can be programmatically changed to display one of two tables. This is useful in user interfaces 8 | * that need to display alternate layouts depending on certain criteria. 9 | */ 10 | public class ToggleGroup extends Container { 11 | public Table table1 = new Table(); 12 | public Table table2 = new Table(); 13 | public boolean showingTable1; 14 | 15 | public ToggleGroup() { 16 | showTable1(); 17 | fill(); 18 | } 19 | 20 | public void showTable1() { 21 | setActor(table1); 22 | showingTable1 = true; 23 | } 24 | 25 | public void showTable2() { 26 | setActor(table2); 27 | showingTable1 = false; 28 | } 29 | 30 | public void swap() { 31 | if (showingTable1) showTable2(); 32 | else showTable1(); 33 | } 34 | 35 | @Override @Deprecated 36 | public void setActor(Table table) { 37 | super.setActor(table); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/widgets/WelcomeCard.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.widgets; 2 | 3 | import com.badlogic.gdx.scenes.scene2d.ui.Button; 4 | import com.badlogic.gdx.scenes.scene2d.ui.Image; 5 | import com.badlogic.gdx.scenes.scene2d.ui.Label; 6 | import com.badlogic.gdx.scenes.scene2d.ui.TextButton; 7 | import com.badlogic.gdx.scenes.scene2d.utils.Drawable; 8 | 9 | import static com.ray3k.gdxparticleeditor.Core.skin; 10 | 11 | /** 12 | * A clickable button to be used with the welcome screen. It consists of a title, an image, description, and a child 13 | * TextButton. 14 | */ 15 | public class WelcomeCard extends Button { 16 | public WelcomeCard(String title, String subtitle, Drawable drawable, String buttonText) { 17 | super(skin, "card"); 18 | 19 | top().left(); 20 | 21 | defaults().space(15); 22 | var label = new Label(title, skin, "bold"); 23 | add(label).left().expandX(); 24 | 25 | row(); 26 | var image = new Image(drawable); 27 | add(image).size(240, 190); 28 | 29 | row(); 30 | label = new Label(subtitle, skin); 31 | add(label); 32 | 33 | row(); 34 | var textButton = new TextButton(buttonText, skin, "highlighted-card"); 35 | add(textButton).expandX().right(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/widgets/panels/StartPanel.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.widgets.panels; 2 | 3 | import com.badlogic.gdx.Gdx; 4 | import com.badlogic.gdx.math.Vector2; 5 | import com.badlogic.gdx.scenes.scene2d.Touchable; 6 | import com.badlogic.gdx.scenes.scene2d.ui.Label; 7 | import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane; 8 | import com.badlogic.gdx.scenes.scene2d.ui.Table; 9 | import com.badlogic.gdx.scenes.scene2d.ui.TextButton; 10 | import com.ray3k.gdxparticleeditor.widgets.Panel; 11 | 12 | import static com.ray3k.gdxparticleeditor.Core.skin; 13 | import static com.ray3k.gdxparticleeditor.Listeners.*; 14 | 15 | /** 16 | * An introductory screen used exclusively in Wizard mode to help introduce users to GDX Particle Editor. 17 | */ 18 | public class StartPanel extends Panel { 19 | private Table scrollTable; 20 | public static StartPanel summaryPanel; 21 | private ScrollPane scrollPane; 22 | private static final Vector2 temp = new Vector2(); 23 | 24 | public StartPanel() { 25 | summaryPanel = this; 26 | setTouchable(Touchable.enabled); 27 | 28 | var label = new Label("Start", skin, "header"); 29 | tabTable.add(label); 30 | 31 | bodyTable.defaults().space(5); 32 | scrollTable = new Table(); 33 | scrollPane = new ScrollPane(scrollTable, skin, "emitter-properties"); 34 | scrollPane.setFlickScroll(false); 35 | bodyTable.add(scrollPane).grow(); 36 | addScrollFocusListener(scrollPane); 37 | 38 | populateScrollTable(); 39 | } 40 | 41 | public void populateScrollTable() { 42 | scrollTable.clearChildren(true); 43 | scrollTable.defaults().space(10); 44 | 45 | var label = new Label("Welcome!", skin, "bold"); 46 | scrollTable.add(label); 47 | 48 | scrollTable.row(); 49 | label = new Label("GDX Particle Editor allows you to create particle effects for your libGDX games.", skin); 50 | scrollTable.add(label); 51 | 52 | scrollTable.row(); 53 | var table = new Table(); 54 | table.columnDefaults(0).right(); 55 | table.columnDefaults(1).left(); 56 | scrollTable.add(table).padTop(10); 57 | 58 | var titleLabel = new Label("Effect Emitters:", skin, "header"); 59 | titleLabel.setColor(skin.getColor("selection")); 60 | table.add(titleLabel); 61 | 62 | label = new Label(" load and create the emitters that comprise your particle effect", skin); 63 | table.add(label); 64 | 65 | table.row(); 66 | titleLabel = new Label("Emitter Properties:", skin, "header"); 67 | titleLabel.setColor(skin.getColor("selection")); 68 | table.add(titleLabel); 69 | 70 | label = new Label(" edit the behavior and appearance of the selected emitter", skin); 71 | table.add(label); 72 | 73 | table.row(); 74 | titleLabel = new Label("Summary:", skin, "header"); 75 | titleLabel.setColor(skin.getColor("selection")); 76 | table.add(titleLabel); 77 | 78 | label = new Label(" save your particle effect to a file that can be loaded in libGDX", skin); 79 | table.add(label); 80 | 81 | scrollTable.row(); 82 | label = new Label("Learn", skin, "bold"); 83 | scrollTable.add(label).padTop(20); 84 | 85 | scrollTable.row(); 86 | table = new Table(); 87 | table.defaults().space(10); 88 | scrollTable.add(table); 89 | 90 | var textButton = new TextButton("GitHub", skin); 91 | table.add(textButton); 92 | addHandListener(textButton); 93 | onChange(textButton, () -> { 94 | Gdx.net.openURI("https://github.com/libgdx/gdx-particle-editor"); 95 | }); 96 | 97 | textButton = new TextButton("YouTube", skin); 98 | table.add(textButton); 99 | addHandListener(textButton); 100 | onChange(textButton, () -> { 101 | 102 | }); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/widgets/panels/SummaryPanel.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.widgets.panels; 2 | 3 | import com.badlogic.gdx.math.Vector2; 4 | import com.badlogic.gdx.scenes.scene2d.Touchable; 5 | import com.badlogic.gdx.scenes.scene2d.ui.Label; 6 | import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane; 7 | import com.badlogic.gdx.scenes.scene2d.ui.Table; 8 | import com.badlogic.gdx.scenes.scene2d.ui.TextButton; 9 | import com.ray3k.gdxparticleeditor.widgets.Panel; 10 | 11 | import static com.ray3k.gdxparticleeditor.Core.*; 12 | import static com.ray3k.gdxparticleeditor.Listeners.*; 13 | 14 | /** 15 | * A summary screen used exclusively in wizard mode that displays some emitter statistics and gives the user an option 16 | * to save. 17 | */ 18 | public class SummaryPanel extends Panel { 19 | private Table scrollTable; 20 | public static SummaryPanel summaryPanel; 21 | private ScrollPane scrollPane; 22 | private static final Vector2 temp = new Vector2(); 23 | 24 | public SummaryPanel() { 25 | summaryPanel = this; 26 | setTouchable(Touchable.enabled); 27 | 28 | var label = new Label("Summary", skin, "header"); 29 | tabTable.add(label); 30 | 31 | bodyTable.defaults().space(5); 32 | scrollTable = new Table(); 33 | scrollPane = new ScrollPane(scrollTable, skin, "emitter-properties"); 34 | scrollPane.setFlickScroll(false); 35 | bodyTable.add(scrollPane).grow(); 36 | addScrollFocusListener(scrollPane); 37 | 38 | populateScrollTable(); 39 | } 40 | 41 | public void populateScrollTable() { 42 | scrollTable.clearChildren(true); 43 | scrollTable.defaults().space(10); 44 | 45 | var label = new Label("Congratulations! You have created a particle effect. It looks great!", skin); 46 | scrollTable.add(label); 47 | 48 | scrollTable.row(); 49 | var table = new Table(); 50 | scrollTable.add(table); 51 | 52 | table.defaults().spaceRight(10).uniformX(); 53 | table.columnDefaults(0).right(); 54 | table.columnDefaults(1).left(); 55 | label = new Label("Emitters:", skin, "header"); 56 | label.setColor(skin.getColor("selection")); 57 | table.add(label); 58 | 59 | label = new Label(Integer.toString(activeEmitters.size), skin); 60 | table.add(label); 61 | 62 | table.row(); 63 | label = new Label("Images:", skin, "header"); 64 | label.setColor(skin.getColor("selection")); 65 | table.add(label); 66 | 67 | label = new Label(Integer.toString(fileHandles.size), skin); 68 | table.add(label); 69 | 70 | table.row(); 71 | label = new Label("Max particle count:", skin, "header"); 72 | label.setColor(skin.getColor("selection")); 73 | table.add(label); 74 | 75 | label = new Label(Integer.toString(maxParticleCount), skin); 76 | table.add(label); 77 | 78 | scrollTable.row(); 79 | label = new Label("Save Particle Effect?", skin, "bold"); 80 | scrollTable.add(label).padTop(20); 81 | 82 | scrollTable.row(); 83 | var textButton = new TextButton("Save", skin); 84 | scrollTable.add(textButton); 85 | addHandListener(textButton); 86 | onChange(textButton, saveRunnable); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/widgets/poptables/PopConfirmClose.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.widgets.poptables; 2 | 3 | import com.badlogic.gdx.Gdx; 4 | import com.badlogic.gdx.Input.Keys; 5 | import com.badlogic.gdx.InputProcessor; 6 | import com.badlogic.gdx.scenes.scene2d.Event; 7 | import com.badlogic.gdx.scenes.scene2d.ui.Label; 8 | import com.badlogic.gdx.scenes.scene2d.ui.Table; 9 | import com.badlogic.gdx.scenes.scene2d.ui.TextButton; 10 | import com.badlogic.gdx.scenes.scene2d.ui.Window.WindowStyle; 11 | import com.badlogic.gdx.utils.Align; 12 | import com.ray3k.gdxparticleeditor.Listeners; 13 | import com.ray3k.gdxparticleeditor.runnables.SaveAsRunnable; 14 | import com.ray3k.gdxparticleeditor.runnables.SaveRunnable; 15 | import com.ray3k.stripe.PopTable; 16 | 17 | import static com.ray3k.gdxparticleeditor.Core.foregroundStage; 18 | import static com.ray3k.gdxparticleeditor.Core.skin; 19 | import static com.ray3k.gdxparticleeditor.Listeners.*; 20 | import static com.ray3k.gdxparticleeditor.Listeners.addHandListener; 21 | import static com.ray3k.gdxparticleeditor.Listeners.onChange; 22 | 23 | /** 24 | * PopTable used to display errors during runtime that can be recovered from. These are typically file errors when 25 | * saving or opening particles and images. A link to the log directory enables users to retrieve the crash log file 26 | * through their OS file explorer. 27 | */ 28 | public class PopConfirmClose extends PopTable { 29 | private final InputProcessor previousInputProcessor; 30 | 31 | public PopConfirmClose() { 32 | super(skin.get(WindowStyle.class)); 33 | 34 | setHideOnUnfocus(true); 35 | key(Keys.ESCAPE, this::hide); 36 | key(Keys.ENTER, this::save); 37 | key(Keys.NUMPAD_ENTER, this::save); 38 | 39 | previousInputProcessor = Gdx.input.getInputProcessor(); 40 | Gdx.input.setInputProcessor(foregroundStage); 41 | 42 | addListener(new TableShowHideListener() { 43 | @Override 44 | public void tableShown(Event event) { 45 | hideAllTooltips(); 46 | } 47 | 48 | @Override 49 | public void tableHidden(Event event) { 50 | if (Gdx.input.getInputProcessor() == foregroundStage) Gdx.input.setInputProcessor(previousInputProcessor); 51 | } 52 | }); 53 | populate(); 54 | } 55 | 56 | private void populate() { 57 | clearChildren(); 58 | pad(20); 59 | defaults().space(10); 60 | 61 | var label = new Label("Save Changes?", skin, "bold"); 62 | add(label); 63 | 64 | row(); 65 | label = new Label("Do you want to save your changes before you quit?", skin); 66 | label.setWrap(true); 67 | label.setAlignment(Align.center); 68 | add(label).growX(); 69 | 70 | row(); 71 | var table = new Table(); 72 | table.defaults().uniformX().fillX().space(10); 73 | add(table); 74 | 75 | var textButton = new TextButton("Save changes", skin, "highlighted"); 76 | table.add(textButton); 77 | addHandListener(textButton); 78 | onChange(textButton, () -> { 79 | save(); 80 | }); 81 | 82 | textButton = new TextButton("Close without saving", skin, "highlighted-red"); 83 | table.add(textButton); 84 | addHandListener(textButton); 85 | onChange(textButton, Gdx.app::exit); 86 | 87 | textButton = new TextButton("Cancel", skin); 88 | table.add(textButton); 89 | addHandListener(textButton); 90 | onChange(textButton, () -> { 91 | hide(); 92 | }); 93 | } 94 | 95 | private void save() { 96 | hide(); 97 | 98 | var saveFirstRunnable = new SaveRunnable(); 99 | var saveAsFirstRunnable = new SaveAsRunnable(); 100 | 101 | saveFirstRunnable.setSaveAsRunnable(saveAsFirstRunnable); 102 | saveAsFirstRunnable.setSaveRunnable(saveFirstRunnable); 103 | saveFirstRunnable.setOnCompletionRunnable(() -> Gdx.app.exit()); 104 | saveFirstRunnable.run(); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/widgets/poptables/PopConfirmLoad.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.widgets.poptables; 2 | 3 | import com.badlogic.gdx.Gdx; 4 | import com.badlogic.gdx.Input.Keys; 5 | import com.badlogic.gdx.InputProcessor; 6 | import com.badlogic.gdx.scenes.scene2d.Event; 7 | import com.badlogic.gdx.scenes.scene2d.ui.Label; 8 | import com.badlogic.gdx.scenes.scene2d.ui.Table; 9 | import com.badlogic.gdx.scenes.scene2d.ui.TextButton; 10 | import com.badlogic.gdx.scenes.scene2d.ui.Window.WindowStyle; 11 | import com.badlogic.gdx.utils.Align; 12 | import com.ray3k.gdxparticleeditor.Listeners; 13 | import com.ray3k.gdxparticleeditor.Utils; 14 | import com.ray3k.stripe.PopTable; 15 | 16 | import static com.ray3k.gdxparticleeditor.Core.foregroundStage; 17 | import static com.ray3k.gdxparticleeditor.Core.skin; 18 | import static com.ray3k.gdxparticleeditor.Listeners.*; 19 | import static com.ray3k.gdxparticleeditor.Listeners.addHandListener; 20 | import static com.ray3k.gdxparticleeditor.Listeners.onChange; 21 | 22 | /** 23 | * PopTable used to display errors during runtime that can be recovered from. These are typically file errors when 24 | * saving or opening particles and images. A link to the log directory enables users to retrieve the crash log file 25 | * through their OS file explorer. 26 | */ 27 | public class PopConfirmLoad extends PopTable { 28 | private final InputProcessor previousInputProcessor; 29 | private Runnable runnableSave; 30 | private Runnable runnableDiscard; 31 | 32 | public PopConfirmLoad(Runnable runnableSave, Runnable runnableDiscard) { 33 | super(skin.get(WindowStyle.class)); 34 | 35 | setHideOnUnfocus(true); 36 | key(Keys.ESCAPE, this::hide); 37 | key(Keys.ENTER, this::save); 38 | key(Keys.NUMPAD_ENTER, this::save); 39 | 40 | this.runnableSave = runnableSave; 41 | this.runnableDiscard = runnableDiscard; 42 | 43 | addListener(new TableShowHideListener() { 44 | @Override 45 | public void tableShown(Event event) { 46 | hideAllTooltips(); 47 | } 48 | 49 | @Override 50 | public void tableHidden(Event event) { 51 | if (Gdx.input.getInputProcessor() == foregroundStage) Gdx.input.setInputProcessor(previousInputProcessor); 52 | } 53 | }); 54 | 55 | previousInputProcessor = Gdx.input.getInputProcessor(); 56 | Gdx.input.setInputProcessor(foregroundStage); 57 | populate(); 58 | } 59 | 60 | private void populate() { 61 | clearChildren(); 62 | pad(20); 63 | defaults().space(10); 64 | 65 | var label = new Label("Save Changes?", skin, "bold"); 66 | add(label); 67 | 68 | row(); 69 | label = new Label("Do you want to save your changes before loading the particle effect?", skin); 70 | label.setWrap(true); 71 | label.setAlignment(Align.center); 72 | add(label).growX(); 73 | 74 | row(); 75 | var table = new Table(); 76 | table.defaults().uniformX().fillX().space(10); 77 | add(table); 78 | 79 | var textButton = new TextButton("Save changes", skin, "highlighted"); 80 | table.add(textButton); 81 | addHandListener(textButton); 82 | onChange(textButton, () -> { 83 | save(); 84 | }); 85 | 86 | textButton = new TextButton("Discard changes", skin, "highlighted-red"); 87 | table.add(textButton); 88 | addHandListener(textButton); 89 | onChange(textButton, () -> { 90 | hide(); 91 | Gdx.input.setInputProcessor(previousInputProcessor); 92 | runnableDiscard.run(); 93 | }); 94 | 95 | textButton = new TextButton("Cancel", skin); 96 | table.add(textButton); 97 | addHandListener(textButton); 98 | onChange(textButton, () -> { 99 | hide(); 100 | Gdx.input.setInputProcessor(previousInputProcessor); 101 | }); 102 | } 103 | 104 | private void save() { 105 | hide(); 106 | Gdx.input.setInputProcessor(previousInputProcessor); 107 | runnableSave.run(); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/widgets/poptables/PopError.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.widgets.poptables; 2 | 3 | import com.badlogic.gdx.Gdx; 4 | import com.badlogic.gdx.Input.Keys; 5 | import com.badlogic.gdx.InputProcessor; 6 | import com.badlogic.gdx.graphics.Color; 7 | import com.badlogic.gdx.scenes.scene2d.Action; 8 | import com.badlogic.gdx.scenes.scene2d.ui.Label; 9 | import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane; 10 | import com.badlogic.gdx.scenes.scene2d.ui.Table; 11 | import com.badlogic.gdx.scenes.scene2d.ui.TextButton; 12 | import com.badlogic.gdx.scenes.scene2d.ui.Window.WindowStyle; 13 | import com.ray3k.gdxparticleeditor.Listeners; 14 | import com.ray3k.gdxparticleeditor.Utils; 15 | import com.ray3k.stripe.PopTable; 16 | 17 | import java.io.IOException; 18 | 19 | import static com.ray3k.gdxparticleeditor.Core.foregroundStage; 20 | import static com.ray3k.gdxparticleeditor.Core.skin; 21 | import static com.ray3k.gdxparticleeditor.Settings.logFile; 22 | 23 | /** 24 | * PopTable used to display errors during runtime that can be recovered from. These are typically file errors when 25 | * saving or opening particles and images. A link to the log directory enables users to retrieve the crash log file 26 | * through their OS file explorer. 27 | */ 28 | public class PopError extends PopTable { 29 | private String message; 30 | private String error; 31 | private final InputProcessor previousInputProcessor; 32 | 33 | public PopError(String message, String error) { 34 | super(skin.get(WindowStyle.class)); 35 | 36 | setHideOnUnfocus(true); 37 | key(Keys.ESCAPE, this::hide); 38 | 39 | this.message = message; 40 | this.error = error; 41 | 42 | populate(); 43 | previousInputProcessor = Gdx.input.getInputProcessor(); 44 | Gdx.input.setInputProcessor(foregroundStage); 45 | } 46 | 47 | @Override 48 | public void hide(Action action) { 49 | super.hide(action); 50 | if (Gdx.input.getInputProcessor() == foregroundStage) Gdx.input.setInputProcessor(previousInputProcessor); 51 | } 52 | 53 | private void populate() { 54 | clearChildren(); 55 | pad(20); 56 | defaults().space(10); 57 | 58 | var label = new Label("GDX Particle Editor encountered an error:", skin, "bold"); 59 | add(label); 60 | 61 | row(); 62 | label = new Label(message, skin); 63 | label.setWrap(true); 64 | add(label).growX(); 65 | 66 | row(); 67 | var scrollPane = new ScrollPane(null, skin); 68 | add(scrollPane).grow(); 69 | 70 | label = new Label(error, skin); 71 | label.setColor(Color.RED); 72 | scrollPane.setActor(label); 73 | 74 | row(); 75 | var table = new Table(); 76 | table.defaults().uniformX().fillX().space(10); 77 | add(table); 78 | 79 | var textButton = new TextButton("Close", skin); 80 | table.add(textButton); 81 | Listeners.addHandListener(textButton); 82 | Listeners.onChange(textButton, () -> { 83 | hide(); 84 | }); 85 | 86 | textButton = new TextButton("Open log", skin); 87 | table.add(textButton); 88 | Listeners.addHandListener(textButton); 89 | Listeners.onChange(textButton, () -> { 90 | try { 91 | Utils.openFileExplorer(logFile); 92 | } catch (IOException e) { 93 | e.printStackTrace(); 94 | } 95 | 96 | hide(); 97 | }); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/widgets/poptables/PopImageError.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.widgets.poptables; 2 | 3 | import com.badlogic.gdx.Gdx; 4 | import com.badlogic.gdx.Input.Keys; 5 | import com.badlogic.gdx.InputProcessor; 6 | import com.badlogic.gdx.files.FileHandle; 7 | import com.badlogic.gdx.graphics.Color; 8 | import com.badlogic.gdx.graphics.Cursor.SystemCursor; 9 | import com.badlogic.gdx.scenes.scene2d.Action; 10 | import com.badlogic.gdx.scenes.scene2d.Stage; 11 | import com.badlogic.gdx.scenes.scene2d.ui.Label; 12 | import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane; 13 | import com.badlogic.gdx.scenes.scene2d.ui.Table; 14 | import com.badlogic.gdx.scenes.scene2d.ui.TextButton; 15 | import com.badlogic.gdx.scenes.scene2d.ui.Window.WindowStyle; 16 | import com.ray3k.gdxparticleeditor.Core; 17 | import com.ray3k.gdxparticleeditor.Listeners; 18 | import com.ray3k.gdxparticleeditor.Utils; 19 | import com.ray3k.stripe.PopTable; 20 | 21 | import java.io.IOException; 22 | 23 | import static com.ray3k.gdxparticleeditor.Core.foregroundStage; 24 | import static com.ray3k.gdxparticleeditor.Core.skin; 25 | import static com.ray3k.gdxparticleeditor.Settings.logFile; 26 | 27 | /** 28 | * PopTable used to display errors during runtime that can be recovered from. These are typically file errors when 29 | * saving or opening particles and images. A link to the log directory enables users to retrieve the crash log file 30 | * through their OS file explorer. 31 | */ 32 | public class PopImageError extends PopTable { 33 | private String message; 34 | private String error; 35 | private boolean merge; 36 | private final InputProcessor previousInputProcessor; 37 | 38 | public PopImageError(String message, String error, FileHandle particleFile, boolean merge) { 39 | super(skin.get(WindowStyle.class)); 40 | 41 | setHideOnUnfocus(true); 42 | key(Keys.ESCAPE, this::hide); 43 | 44 | this.message = message; 45 | this.error = error; 46 | this.merge = merge; 47 | 48 | populate(particleFile); 49 | previousInputProcessor = Gdx.input.getInputProcessor(); 50 | Gdx.input.setInputProcessor(foregroundStage); 51 | } 52 | 53 | @Override 54 | public void hide(Action action) { 55 | super.hide(action); 56 | if (Gdx.input.getInputProcessor() == foregroundStage) Gdx.input.setInputProcessor(previousInputProcessor); 57 | } 58 | 59 | private void populate(FileHandle particleFile) { 60 | clearChildren(); 61 | pad(20); 62 | defaults().space(10); 63 | 64 | var label = new Label("GDX Particle Editor encountered an error:", skin, "bold"); 65 | add(label); 66 | 67 | row(); 68 | label = new Label(message, skin); 69 | label.setWrap(true); 70 | add(label).growX(); 71 | 72 | row(); 73 | var scrollPane = new ScrollPane(null, skin); 74 | add(scrollPane).grow(); 75 | 76 | label = new Label(error, skin); 77 | label.setColor(Color.RED); 78 | scrollPane.setActor(label); 79 | 80 | row(); 81 | var table = new Table(); 82 | table.defaults().uniformX().fillX().space(10); 83 | add(table); 84 | 85 | var textButton = new TextButton("Locate images...", skin); 86 | table.add(textButton); 87 | Listeners.addHandListener(textButton); 88 | Listeners.onChange(textButton, () -> { 89 | hide(); 90 | Gdx.graphics.setSystemCursor(SystemCursor.Arrow); 91 | 92 | var pop = new PopLocateImages(particleFile, merge); 93 | pop.show(foregroundStage); 94 | }); 95 | 96 | textButton = new TextButton("Close", skin); 97 | table.add(textButton); 98 | Listeners.addHandListener(textButton); 99 | Listeners.onChange(textButton, () -> { 100 | hide(); 101 | }); 102 | 103 | textButton = new TextButton("Open log", skin); 104 | table.add(textButton); 105 | Listeners.addHandListener(textButton); 106 | Listeners.onChange(textButton, () -> { 107 | try { 108 | Utils.openFileExplorer(logFile); 109 | } catch (IOException e) { 110 | e.printStackTrace(); 111 | } 112 | 113 | hide(); 114 | }); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/widgets/styles/PPcolorGraphStyle.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.widgets.styles; 2 | 3 | import com.ray3k.gdxparticleeditor.widgets.ColorGraph.ColorGraphStyle; 4 | import com.ray3k.tenpatch.TenPatchDrawable; 5 | 6 | import static com.ray3k.gdxparticleeditor.Core.skin; 7 | 8 | public class PPcolorGraphStyle extends ColorGraphStyle { 9 | public PPcolorGraphStyle() { 10 | background = skin.getDrawable("colorbar-bg-10"); 11 | nodeStartUp = skin.getDrawable("colorbar-left-bg"); 12 | nodeStartOver = null; 13 | nodeStartDown = null; 14 | nodeStartFill = skin.getDrawable("colorbar-left-fill"); 15 | nodeUp = skin.getDrawable("colorbar-knob-bg"); 16 | nodeOver = null; 17 | nodeDown = null; 18 | nodeFill = skin.getDrawable("colorbar-knob-fill"); 19 | nodeEndUp = skin.getDrawable("colorbar-right-bg"); 20 | nodeEndOver = null; 21 | nodeEndDown = null; 22 | nodeEndFill = skin.getDrawable("colorbar-right-fill"); 23 | white = skin.get("colorbar-white-10", TenPatchDrawable.class); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/widgets/styles/PPcolorPickerStyle.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.widgets.styles; 2 | 3 | import com.badlogic.gdx.scenes.scene2d.ui.ImageButton.ImageButtonStyle; 4 | import com.badlogic.gdx.scenes.scene2d.ui.Label.LabelStyle; 5 | import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane.ScrollPaneStyle; 6 | import com.badlogic.gdx.scenes.scene2d.ui.TextButton.TextButtonStyle; 7 | import com.badlogic.gdx.scenes.scene2d.ui.TextField.TextFieldStyle; 8 | import com.ray3k.stripe.PopColorPicker.PopColorPickerStyle; 9 | 10 | import static com.ray3k.gdxparticleeditor.Core.skin; 11 | 12 | public class PPcolorPickerStyle extends PopColorPickerStyle { 13 | public PPcolorPickerStyle() { 14 | background = skin.getDrawable("cp-background-10"); 15 | stageBackground = skin.getDrawable("stage-background"); 16 | titleBarBackground = skin.getDrawable("cp-header-10"); 17 | labelStyle = skin.get(LabelStyle.class); 18 | textButtonStyle = skin.get(TextButtonStyle.class); 19 | fileTextButtonStyle = skin.get("cp-file", TextButtonStyle.class); 20 | scrollPaneStyle = skin.get(ScrollPaneStyle.class); 21 | colorSwatch = skin.getDrawable("cp-color-swatch"); 22 | colorSwatchNew = skin.getDrawable("cp-color-swatch-new"); 23 | colorSwatchPopBackground = skin.getDrawable("cp-color-swatch-pop-background-10"); 24 | previewSwatchBackground = skin.getDrawable("cp-swatch"); 25 | previewSwatchOld = skin.getDrawable("cp-swatch-old"); 26 | previewSwatchNew = skin.getDrawable("cp-swatch-new"); 27 | previewSwatchSingleBackground = skin.getDrawable("cp-swatch-null"); 28 | previewSwatchSingle = skin.getDrawable("cp-swatch-new-null"); 29 | textFieldStyle = skin.get(TextFieldStyle.class); 30 | hexTextFieldStyle = skin.get("cp", TextFieldStyle.class); 31 | colorSwatchPopPreview = skin.getDrawable("cp-color-swatch-pop-preview"); 32 | colorSliderBackground = skin.getDrawable("cp-slider-background-10"); 33 | colorSliderKnobHorizontal = skin.getDrawable("cp-slider-knob-horizontal"); 34 | colorSliderKnobVertical = skin.getDrawable("cp-slider-knob-vertical"); 35 | colorKnobCircleBackground = skin.getDrawable("cp-color-knob-circle-background"); 36 | colorKnobCircleForeground = skin.getDrawable("cp-color-circle-knob-foreground"); 37 | radioButtonStyle = skin.get("cp-radio", ImageButtonStyle.class); 38 | increaseButtonStyle = skin.get("cp-up", ImageButtonStyle.class); 39 | decreaseButtonStyle = skin.get("cp-down", ImageButtonStyle.class); 40 | checkerBackground = skin.getDrawable("cp-checker-10"); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/widgets/styles/PPdraggableListStyle.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.widgets.styles; 2 | 3 | import com.ray3k.stripe.DraggableList.DraggableListStyle; 4 | 5 | import static com.ray3k.gdxparticleeditor.Core.skin; 6 | 7 | public class PPdraggableListStyle extends DraggableListStyle { 8 | public PPdraggableListStyle() { 9 | dividerUp = skin.getDrawable("draggable-list-divider-10"); 10 | dividerOver = skin.getDrawable("draggable-list-divider-over-10"); 11 | transparencyOnDrag = .25f; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/widgets/styles/PPdraggableTextListNoBGStyle.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.widgets.styles; 2 | 3 | import com.ray3k.stripe.DraggableTextList.DraggableTextListStyle; 4 | 5 | import static com.ray3k.gdxparticleeditor.Core.skin; 6 | 7 | public class PPdraggableTextListNoBGStyle extends DraggableTextListStyle { 8 | public PPdraggableTextListNoBGStyle() { 9 | dividerUp = skin.getDrawable("draggable-list-divider-10"); 10 | dividerOver = skin.getDrawable("draggable-list-divider-over-10"); 11 | validBackgroundUp = skin.getDrawable("draggable-list-drag-background-10"); 12 | dragBackgroundUp = skin.getDrawable("draggable-list-drag-remove-10"); 13 | transparencyOnDrag = .25f; 14 | 15 | font = skin.getFont("font-black"); 16 | textBackgroundUp = skin.getDrawable("list-selection-invisible"); 17 | textBackgroundDown = skin.getDrawable("list-selection-invisible"); 18 | textBackgroundOver = skin.getDrawable("list-over-10"); 19 | textBackgroundChecked = skin.getDrawable("list-selection-10"); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/widgets/styles/PPdraggableTextListStyle.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.widgets.styles; 2 | 3 | import com.ray3k.stripe.DraggableTextList.DraggableTextListStyle; 4 | 5 | import static com.ray3k.gdxparticleeditor.Core.skin; 6 | 7 | public class PPdraggableTextListStyle extends DraggableTextListStyle { 8 | public PPdraggableTextListStyle() { 9 | dividerUp = skin.getDrawable("draggable-list-divider-10"); 10 | dividerOver = skin.getDrawable("draggable-list-divider-over-10"); 11 | background = skin.getDrawable("draggable-list-10"); 12 | validBackgroundUp = skin.getDrawable("draggable-list-drag-background-10"); 13 | dragBackgroundUp = skin.getDrawable("draggable-list-drag-remove-10"); 14 | transparencyOnDrag = .25f; 15 | 16 | font = skin.getFont("font-black"); 17 | textBackgroundUp = skin.getDrawable("list-selection-invisible"); 18 | textBackgroundDown = skin.getDrawable("list-selection-invisible"); 19 | textBackgroundOver = skin.getDrawable("list-selection-10"); 20 | textBackgroundChecked = null; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/widgets/styles/PPeditableLabelStyle.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.widgets.styles; 2 | 3 | import com.badlogic.gdx.scenes.scene2d.ui.Label.LabelStyle; 4 | import com.badlogic.gdx.scenes.scene2d.ui.TextField.TextFieldStyle; 5 | import com.ray3k.gdxparticleeditor.widgets.EditableLabel.EditableLabelStyle; 6 | 7 | import static com.ray3k.gdxparticleeditor.Core.skin; 8 | 9 | public class PPeditableLabelStyle extends EditableLabelStyle { 10 | public PPeditableLabelStyle() { 11 | labelStyle = skin.get(LabelStyle.class); 12 | textFieldStyle = skin.get("editable-label-selection", TextFieldStyle.class); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/widgets/styles/PPinfSliderStyle.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.widgets.styles; 2 | 3 | import com.ray3k.gdxparticleeditor.widgets.InfSlider.InfSliderStyle; 4 | 5 | import static com.ray3k.gdxparticleeditor.Core.skin; 6 | 7 | public class PPinfSliderStyle extends InfSliderStyle { 8 | public PPinfSliderStyle() { 9 | background = skin.getDrawable("slider-bg-10"); 10 | knobUp = skin.getDrawable("slider-knob"); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/widgets/styles/PPlineGraphBigStyle.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.widgets.styles; 2 | 3 | import com.badlogic.gdx.graphics.Color; 4 | import com.badlogic.gdx.scenes.scene2d.ui.Label.LabelStyle; 5 | import com.ray3k.gdxparticleeditor.widgets.LineGraph.LineGraphStyle; 6 | 7 | import static com.ray3k.gdxparticleeditor.Core.skin; 8 | 9 | public class PPlineGraphBigStyle extends LineGraphStyle { 10 | public PPlineGraphBigStyle() { 11 | background = skin.getDrawable("graph-big-bg-10"); 12 | backgroundLabelStyle = skin.get("graph", LabelStyle.class); 13 | knobLabelStyle = skin.get("graph-node", LabelStyle.class); 14 | nodeUp = skin.getDrawable("graph-node-up"); 15 | nodeDown = skin.getDrawable("graph-node-over"); 16 | nodeOver = skin.getDrawable("graph-node-over"); 17 | lineColor = Color.valueOf("0074ff"); 18 | lineWidth = 2; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/widgets/styles/PPlineGraphStyle.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.widgets.styles; 2 | 3 | import com.badlogic.gdx.graphics.Color; 4 | import com.badlogic.gdx.scenes.scene2d.ui.Label.LabelStyle; 5 | import com.ray3k.gdxparticleeditor.widgets.LineGraph.LineGraphStyle; 6 | 7 | import static com.ray3k.gdxparticleeditor.Core.skin; 8 | 9 | public class PPlineGraphStyle extends LineGraphStyle { 10 | public PPlineGraphStyle() { 11 | background = skin.getDrawable("graph-bg-10"); 12 | backgroundLabelStyle = skin.get("graph", LabelStyle.class); 13 | nodeUp = skin.getDrawable("graph-node-up"); 14 | nodeDown = skin.getDrawable("graph-node-over"); 15 | nodeOver = skin.getDrawable("graph-node-over"); 16 | lineColor = Color.valueOf("0074ff"); 17 | lineWidth = 2; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/widgets/styles/PPresizeWidgetStyle.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.widgets.styles; 2 | 3 | import com.ray3k.stripe.ResizeWidget.ResizeWidgetStyle; 4 | 5 | import static com.ray3k.gdxparticleeditor.Core.skin; 6 | 7 | public class PPresizeWidgetStyle extends ResizeWidgetStyle { 8 | public PPresizeWidgetStyle() { 9 | background = skin.getDrawable("resize-background-10"); 10 | handle = skin.getDrawable("resize-knob-up"); 11 | handleOver = skin.getDrawable("resize-knob-over"); 12 | minorHandle = skin.getDrawable("resize-knob-up"); 13 | minorHandleOver = skin.getDrawable("resize-knob-over"); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/widgets/styles/PPspinnerStyle.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.widgets.styles; 2 | 3 | import com.badlogic.gdx.scenes.scene2d.ui.Button.ButtonStyle; 4 | import com.badlogic.gdx.scenes.scene2d.ui.TextField.TextFieldStyle; 5 | import com.ray3k.stripe.Spinner.SpinnerStyle; 6 | 7 | import static com.ray3k.gdxparticleeditor.Core.skin; 8 | 9 | public class PPspinnerStyle extends SpinnerStyle { 10 | public PPspinnerStyle() { 11 | buttonPlusStyle = skin.get("spinner-top", ButtonStyle.class); 12 | buttonMinusStyle = skin.get("spinner-bottom", ButtonStyle.class); 13 | textFieldStyle = skin.get("spinner", TextFieldStyle.class);; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/widgets/styles/Styles.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.widgets.styles; 2 | 3 | import com.badlogic.gdx.scenes.scene2d.ui.Window.WindowStyle; 4 | import com.ray3k.gdxparticleeditor.widgets.ColorGraph.ColorGraphStyle; 5 | import com.ray3k.gdxparticleeditor.widgets.EditableLabel.EditableLabelStyle; 6 | import com.ray3k.gdxparticleeditor.widgets.InfSlider.InfSliderStyle; 7 | import com.ray3k.gdxparticleeditor.widgets.LineGraph.LineGraphStyle; 8 | import com.ray3k.stripe.DraggableList.DraggableListStyle; 9 | import com.ray3k.stripe.DraggableTextList.DraggableTextListStyle; 10 | import com.ray3k.stripe.PopColorPicker.PopColorPickerStyle; 11 | import com.ray3k.stripe.PopTable.PopTableStyle; 12 | import com.ray3k.stripe.ResizeWidget.ResizeWidgetStyle; 13 | import com.ray3k.stripe.Spinner.SpinnerStyle; 14 | 15 | import static com.ray3k.gdxparticleeditor.Core.skin; 16 | 17 | /** 18 | * Collection of styles for custom widgets that are not provided in the skin JSON. 19 | */ 20 | public class Styles { 21 | public static LineGraphStyle lineGraphStyle; 22 | public static LineGraphStyle lineGraphBigStyle; 23 | public static ColorGraphStyle colorGraphStyle; 24 | public static SpinnerStyle spinnerStyle; 25 | public static ResizeWidgetStyle resizeWidgetStyle; 26 | public static DraggableListStyle draggableListStyle; 27 | public static DraggableTextListStyle draggableTextListStyle; 28 | public static DraggableTextListStyle draggableTextListNoBgStyle; 29 | public static InfSliderStyle infSliderStyle; 30 | public static PopTableStyle tooltipBottomArrowStyle; 31 | public static PopTableStyle tooltipBottomLeftArrowStyle; 32 | public static PopTableStyle tooltipBottomRightArrowStyle; 33 | public static PopTableStyle tooltipTopArrowStyle; 34 | public static PopTableStyle tooltipRightArrowStyle; 35 | public static PopTableStyle tooltipLeftArrowStyle; 36 | public static EditableLabelStyle editableLabelStyle; 37 | public static PopColorPickerStyle popColorPickerStyle; 38 | 39 | public static void initializeStyles() { 40 | popColorPickerStyle = new PPcolorPickerStyle(); 41 | lineGraphStyle = new PPlineGraphStyle(); 42 | lineGraphBigStyle = new PPlineGraphBigStyle(); 43 | colorGraphStyle = new PPcolorGraphStyle(); 44 | spinnerStyle = new PPspinnerStyle(); 45 | resizeWidgetStyle = new PPresizeWidgetStyle(); 46 | draggableListStyle = new PPdraggableListStyle(); 47 | draggableTextListStyle = new PPdraggableTextListStyle(); 48 | draggableTextListNoBgStyle = new PPdraggableTextListNoBGStyle(); 49 | infSliderStyle = new PPinfSliderStyle(); 50 | tooltipBottomArrowStyle = new PopTableStyle(skin.get("tooltip-bottom-arrow", WindowStyle.class)); 51 | tooltipBottomLeftArrowStyle = new PopTableStyle(skin.get("tooltip-bottom-left-arrow", WindowStyle.class)); 52 | tooltipBottomRightArrowStyle = new PopTableStyle(skin.get("tooltip-bottom-right-arrow", WindowStyle.class)); 53 | tooltipTopArrowStyle = new PopTableStyle(skin.get("tooltip-top-arrow", WindowStyle.class)); 54 | tooltipRightArrowStyle = new PopTableStyle(skin.get("tooltip-right-arrow", WindowStyle.class)); 55 | tooltipLeftArrowStyle = new PopTableStyle(skin.get("tooltip-left-arrow", WindowStyle.class)); 56 | editableLabelStyle = new PPeditableLabelStyle(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/widgets/subpanels/CountSubPanel.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.widgets.subpanels; 2 | 3 | import com.badlogic.gdx.scenes.scene2d.Touchable; 4 | import com.badlogic.gdx.scenes.scene2d.ui.Label; 5 | import com.badlogic.gdx.utils.Align; 6 | import com.ray3k.gdxparticleeditor.undo.UndoManager; 7 | import com.ray3k.gdxparticleeditor.undo.undoables.CountMaxUndoable; 8 | import com.ray3k.gdxparticleeditor.undo.undoables.CountMinUndoable; 9 | import com.ray3k.gdxparticleeditor.widgets.Panel; 10 | import com.ray3k.stripe.Spinner; 11 | import com.ray3k.stripe.Spinner.Orientation; 12 | 13 | import static com.ray3k.gdxparticleeditor.Core.selectedEmitter; 14 | import static com.ray3k.gdxparticleeditor.Core.skin; 15 | import static com.ray3k.gdxparticleeditor.Listeners.*; 16 | import static com.ray3k.gdxparticleeditor.widgets.styles.Styles.spinnerStyle; 17 | import static com.ray3k.gdxparticleeditor.widgets.styles.Styles.tooltipBottomArrowStyle; 18 | 19 | /** 20 | * A widget that allows the user to modify the Count value of the particle effect specifically. Numeric spinners for min 21 | * and max. 22 | */ 23 | public class CountSubPanel extends Panel { 24 | public CountSubPanel() { 25 | setTouchable(Touchable.enabled); 26 | 27 | final var itemSpacing = 5; 28 | final var gap = 15; 29 | final var spinnerWidth = 50; 30 | 31 | tabTable.left(); 32 | var label = new Label("Count", skin, "header-underline"); 33 | tabTable.add(label); 34 | 35 | bodyTable.defaults().space(itemSpacing); 36 | bodyTable.left(); 37 | 38 | label = new Label("Min:", skin); 39 | bodyTable.add(label); 40 | 41 | var minSpinner = new Spinner(selectedEmitter.getMinParticleCount(), 1, 0, Orientation.RIGHT_STACK, spinnerStyle); 42 | minSpinner.setHoldIncrement(10); 43 | bodyTable.add(minSpinner).width(spinnerWidth); 44 | addHandListener(minSpinner.getButtonMinus()); 45 | addHandListener(minSpinner.getButtonPlus()); 46 | addIbeamListener(minSpinner.getTextField()); 47 | addUnfocusOnEnterKeyListener(minSpinner); 48 | addTooltip(minSpinner, "The minimum number of particles at all times", Align.top, Align.top, tooltipBottomArrowStyle); 49 | var changeListener = onChange(minSpinner, () -> UndoManager.add(new CountMinUndoable(selectedEmitter, minSpinner.getValueAsInt(), selectedEmitter.getMinParticleCount()))); 50 | addInfiniteSlider(minSpinner, 1, 20, false, changeListener); 51 | 52 | label = new Label("Max:", skin); 53 | bodyTable.add(label).spaceLeft(gap); 54 | 55 | var maxSpinner = new Spinner(selectedEmitter.getMaxParticleCount(), 1, 0, Orientation.RIGHT_STACK, spinnerStyle); 56 | maxSpinner.setHoldIncrement(10); 57 | bodyTable.add(maxSpinner).width(spinnerWidth); 58 | addHandListener(maxSpinner.getButtonMinus()); 59 | addHandListener(maxSpinner.getButtonPlus()); 60 | addIbeamListener(maxSpinner.getTextField()); 61 | addUnfocusOnEnterKeyListener(maxSpinner); 62 | addTooltip(maxSpinner, "The maximum number of particles allowed", Align.top, Align.top, tooltipBottomArrowStyle); 63 | changeListener = onChange(maxSpinner, () -> { 64 | var value = Math.max(0, maxSpinner.getValueAsInt()); 65 | UndoManager.add(new CountMaxUndoable(selectedEmitter, value, selectedEmitter.getMaxParticleCount())); 66 | }); 67 | addInfiniteSlider(maxSpinner, 1, 20, false, changeListener); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/gdxparticleeditor/widgets/tables/WelcomeTable.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.gdxparticleeditor.widgets.tables; 2 | 3 | import com.badlogic.gdx.Gdx; 4 | import com.badlogic.gdx.graphics.Cursor.SystemCursor; 5 | import com.badlogic.gdx.scenes.scene2d.actions.Actions; 6 | import com.badlogic.gdx.scenes.scene2d.ui.*; 7 | import com.badlogic.gdx.utils.Align; 8 | import com.ray3k.gdxparticleeditor.Core; 9 | import com.ray3k.gdxparticleeditor.Settings; 10 | import com.ray3k.gdxparticleeditor.widgets.WelcomeCard; 11 | import com.ray3k.gdxparticleeditor.widgets.poptables.PopEditorSettings; 12 | 13 | import static com.ray3k.gdxparticleeditor.Core.*; 14 | import static com.ray3k.gdxparticleeditor.Listeners.*; 15 | import static com.ray3k.gdxparticleeditor.PresetActions.transition; 16 | import static com.ray3k.gdxparticleeditor.Settings.DEFAULT_OPEN_TO_SCREEN; 17 | import static com.ray3k.gdxparticleeditor.Settings.NAME_OPEN_TO_SCREEN; 18 | import static com.ray3k.gdxparticleeditor.widgets.styles.Styles.tooltipBottomRightArrowStyle; 19 | 20 | /** 21 | * The introductory table which highlights the app title and buttons to open the classic or wizard modes. 22 | */ 23 | public class WelcomeTable extends Table { 24 | private Button settingsButton; 25 | 26 | public WelcomeTable() { 27 | shortcutManager.setDisabled(true); 28 | var stack = new Stack(); 29 | add(stack).grow(); 30 | 31 | var root = new Table(); 32 | stack.add(root); 33 | 34 | var table = new Table(); 35 | root.add(table); 36 | 37 | var image = new Image(skin, "title"); 38 | table.add(image); 39 | 40 | table.row(); 41 | var label = new Label(version, skin); 42 | table.add(label).expandX().right().padRight(10).padTop(5); 43 | 44 | root.row(); 45 | table = new Table(); 46 | root.add(table).spaceTop(100); 47 | 48 | table.row().spaceRight(70); 49 | var classicCard = new WelcomeCard("Classic", "The classic Particle Editor experience", skin.getDrawable("thumb-classic"), "Open Classic Mode"); 50 | table.add(classicCard); 51 | addHandListener(classicCard); 52 | 53 | var wizardCard = new WelcomeCard("Wizard", "Simple with a large preview", skin.getDrawable("thumb-wizard"), "Open Wizard Mode"); 54 | table.add(wizardCard); 55 | addHandListener(wizardCard); 56 | 57 | table.row(); 58 | var checkbox = new CheckBox("Remember my choice", skin); 59 | checkbox.setChecked(!preferences.getString(NAME_OPEN_TO_SCREEN, DEFAULT_OPEN_TO_SCREEN).equals(DEFAULT_OPEN_TO_SCREEN)); 60 | table.add(checkbox).space(25).right().colspan(2); 61 | addHandListener(checkbox); 62 | onChange(checkbox, () -> { 63 | if (!checkbox.isChecked()) { 64 | preferences.putString(NAME_OPEN_TO_SCREEN, "Welcome"); 65 | preferences.flush(); 66 | } 67 | }); 68 | 69 | root = new Table(); 70 | stack.add(root); 71 | 72 | settingsButton = new Button(skin, "settings"); 73 | root.add(settingsButton).expand().bottom().right().padRight(20).padBottom(10); 74 | addHandListener(settingsButton); 75 | addTooltip(settingsButton, "Open the Editor Settings dialog", Align.top, Align.topLeft, tooltipBottomRightArrowStyle, false); 76 | onChange(settingsButton, () -> { 77 | Gdx.input.setInputProcessor(foregroundStage); 78 | Gdx.graphics.setSystemCursor(SystemCursor.Arrow); 79 | var pop = new PopEditorSettings(); 80 | pop.show(foregroundStage); 81 | }); 82 | 83 | onChange(classicCard, () -> { 84 | if (checkbox.isChecked()) { 85 | preferences.putString(NAME_OPEN_TO_SCREEN, "Classic"); 86 | preferences.flush(); 87 | } 88 | Core.openTable = "Classic"; 89 | transition(this, new ClassicTable(), Align.top); 90 | shortcutManager.setDisabled(false); 91 | shortcutManager.setScope(Settings.CLASSIC_SCOPE); 92 | fadeSettingsButton(); 93 | }); 94 | 95 | onChange(wizardCard, () -> { 96 | if (checkbox.isChecked()) { 97 | preferences.putString(NAME_OPEN_TO_SCREEN, "Wizard"); 98 | preferences.flush(); 99 | } 100 | Core.openTable = "Wizard"; 101 | transition(this, new WizardTable(), Align.top); 102 | shortcutManager.setDisabled(false); 103 | shortcutManager.setScope(Settings.WIZARD_SCOPE); 104 | fadeSettingsButton(); 105 | }); 106 | } 107 | 108 | private void fadeSettingsButton() { 109 | settingsButton.addAction(Actions.fadeOut(.3f)); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/stripe/PopTableHoverListener.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.stripe; 2 | 3 | import com.badlogic.gdx.scenes.scene2d.*; 4 | import com.badlogic.gdx.scenes.scene2d.ui.Skin; 5 | import com.badlogic.gdx.scenes.scene2d.utils.Disableable; 6 | import com.ray3k.stripe.PopTable.PopTableStyle; 7 | import com.ray3k.stripe.PopTable.TableShowHideListener; 8 | 9 | public class PopTableHoverListener extends InputListener { 10 | protected PopTable popTable; 11 | public boolean hideOnExit; 12 | private final int align; 13 | private final int edge; 14 | 15 | public PopTableHoverListener(int edge, int align, Skin skin) { 16 | this(edge, align, skin.get(PopTableStyle.class)); 17 | } 18 | 19 | public PopTableHoverListener(int edge, int align, Skin skin, String style) { 20 | this(edge, align, skin.get(style, PopTableStyle.class)); 21 | } 22 | 23 | public PopTableHoverListener(int edge, int align, PopTableStyle style) { 24 | hideOnExit = true; 25 | popTable = new PopTable(style); 26 | popTable.setModal(false); 27 | popTable.setTouchable(Touchable.disabled); 28 | this.edge = edge; 29 | this.align = align; 30 | popTable.addListener(new TableShowHideListener() { 31 | @Override 32 | public void tableShown(Event event) { 33 | PopTableHoverListener.this.tableShown(event); 34 | } 35 | 36 | @Override 37 | public void tableHidden(Event event) { 38 | PopTableHoverListener.this.tableHidden(event); 39 | } 40 | }); 41 | } 42 | 43 | @Override 44 | public void enter(InputEvent event, float x, float y, int pointer, Actor fromActor) { 45 | if (popTable.isHidden()) { 46 | if (fromActor == null || !event.getListenerActor().isAscendantOf(fromActor)) { 47 | Stage stage = event.getListenerActor().getStage(); 48 | Actor actor = event.getListenerActor(); 49 | 50 | if (actor instanceof Disableable) { 51 | if (((Disableable) actor).isDisabled()) return; 52 | } 53 | 54 | popTable.show(stage); 55 | popTable.attachToActor(actor, edge, align); 56 | 57 | 58 | popTable.moveToInsideStage(); 59 | } 60 | } 61 | } 62 | 63 | @Override 64 | public void exit(InputEvent event, float x, float y, int pointer, Actor toActor) { 65 | if (hideOnExit && !popTable.isHidden()) { 66 | if (toActor == null || !event.getListenerActor().isAscendantOf(toActor)) { 67 | popTable.hide(); 68 | } 69 | } 70 | } 71 | 72 | public PopTable getPopTable() { 73 | return popTable; 74 | } 75 | 76 | /** 77 | * Override this method to be performed when the popTable is hidden or dismissed. 78 | * @param event The event associated with the table being shown. 79 | */ 80 | public void tableShown(Event event) { 81 | 82 | } 83 | 84 | /** 85 | * Override this method to be performed when the popTable is hidden or dismissed. 86 | * @param event The event associated with the table being hidden. 87 | */ 88 | public void tableHidden(Event event) { 89 | 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/stripe/ScrollFocusListener.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.stripe; 2 | 3 | import com.badlogic.gdx.scenes.scene2d.Actor; 4 | import com.badlogic.gdx.scenes.scene2d.InputEvent; 5 | import com.badlogic.gdx.scenes.scene2d.InputListener; 6 | import com.badlogic.gdx.scenes.scene2d.Stage; 7 | import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane; 8 | 9 | /** 10 | * Add ScrollFocusListener to any scrollable widget that you want to set as the Stage's scrollFocus once the user mouses 11 | * over the widget. 12 | */ 13 | public class ScrollFocusListener extends InputListener { 14 | private Stage stage; 15 | 16 | public ScrollFocusListener(Stage stage) { 17 | this.stage = stage; 18 | } 19 | 20 | @Override 21 | public void enter(InputEvent event, float x, float y, int pointer, Actor fromActor) { 22 | var scrollFocus = stage.getScrollFocus(); 23 | var actor = event.getListenerActor(); 24 | if (actor instanceof ScrollPane) { 25 | var scrollPane = (ScrollPane) actor; 26 | if (!scrollPane.isScrollY() && !scrollPane.isScrollX()) return; 27 | } 28 | if (scrollFocus == null || scrollFocus != actor && !scrollFocus.isDescendantOf(actor)) { 29 | stage.setScrollFocus(event.getListenerActor()); 30 | } 31 | } 32 | 33 | @Override 34 | public void exit(InputEvent event, float x, float y, int pointer, Actor toActor) { 35 | var actor = event.getListenerActor(); 36 | if (stage.getScrollFocus() == actor && (toActor == null || !toActor.isDescendantOf(actor))) { 37 | stage.setScrollFocus(null); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /core/src/main/java/com/ray3k/stripe/ViewportWidget.java: -------------------------------------------------------------------------------- 1 | package com.ray3k.stripe; 2 | 3 | import com.badlogic.gdx.Gdx; 4 | import com.badlogic.gdx.math.MathUtils; 5 | import com.badlogic.gdx.math.Vector2; 6 | import com.badlogic.gdx.scenes.scene2d.ui.Widget; 7 | import com.badlogic.gdx.utils.viewport.Viewport; 8 | 9 | /** 10 | * A widget that allows you to embed an entire viewport within your UI. This is an excellent tool to integrate your 11 | * games into a UI instead of just overlaying the UI on top of it. Make sure to render your game assets after calling 12 | * stage.act() in the render method to prevent position lag. 13 | */ 14 | public class ViewportWidget extends Widget { 15 | public Viewport viewport; 16 | private final static Vector2 temp = new Vector2(); 17 | 18 | public ViewportWidget(Viewport viewport) { 19 | this.viewport = viewport; 20 | } 21 | 22 | /** 23 | * Updates the viewport's bounds to match the size and position of the widget. Applies the viewport. Call before 24 | * drawing to the viewport. 25 | * @param centerCamera 26 | */ 27 | public void updateViewport(boolean centerCamera) { 28 | temp.set(MathUtils.round(getWidth()), MathUtils.round(getHeight())); 29 | if (getStage() != null) getStage().getViewport().project(temp); 30 | 31 | viewport.update(MathUtils.round(temp.x), MathUtils.round(temp.y), centerCamera); 32 | 33 | int viewportOriginalX = viewport.getScreenX(); 34 | int viewportOriginalY = viewport.getScreenY(); 35 | temp.set(0, 0); 36 | localToScreenCoordinates(temp); 37 | viewport.setScreenPosition(viewportOriginalX + MathUtils.round(temp.x), viewportOriginalY + MathUtils.round(Gdx.graphics.getHeight() - temp.y)); 38 | viewport.apply(centerCamera); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /core/src/main/resources/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libgdx/gdx-particle-editor/9b0aa9c8a2208c510a9d8a6b26c08f80b6aaf7dc/core/src/main/resources/icon128.png -------------------------------------------------------------------------------- /core/src/main/resources/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libgdx/gdx-particle-editor/9b0aa9c8a2208c510a9d8a6b26c08f80b6aaf7dc/core/src/main/resources/icon16.png -------------------------------------------------------------------------------- /core/src/main/resources/icon32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libgdx/gdx-particle-editor/9b0aa9c8a2208c510a9d8a6b26c08f80b6aaf7dc/core/src/main/resources/icon32.png -------------------------------------------------------------------------------- /core/src/main/resources/icon64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libgdx/gdx-particle-editor/9b0aa9c8a2208c510a9d8a6b26c08f80b6aaf7dc/core/src/main/resources/icon64.png -------------------------------------------------------------------------------- /core/src/main/resources/version: -------------------------------------------------------------------------------- 1 | 1.0.9 -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.daemon=true 2 | org.gradle.jvmargs=-Xms512M -Xmx1G -XX:MaxMetaspaceSize=1G 3 | org.gradle.configureondemand=false 4 | lombokVersion=1.18.28 5 | gdxVersion=1.12.1 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libgdx/gdx-particle-editor/9b0aa9c8a2208c510a9d8a6b26c08f80b6aaf7dc/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if %ERRORLEVEL% equ 0 goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if %ERRORLEVEL% equ 0 goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | set EXIT_CODE=%ERRORLEVEL% 84 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 85 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 86 | exit /b %EXIT_CODE% 87 | 88 | :mainEnd 89 | if "%OS%"=="Windows_NT" endlocal 90 | 91 | :omega 92 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include 'core' 2 | --------------------------------------------------------------------------------