diff --git a/.gitignore b/.gitignore index 247f99fc..a3f91888 100644 --- a/.gitignore +++ b/.gitignore @@ -53,6 +53,7 @@ dist/ nbdist/ nbactions.xml nb-configuration.xml +/.nb-gradle/ ## Gradle @@ -65,3 +66,5 @@ build/ /.nb-gradle/ core/assets/temp/ core/assets/imagefont/ +desktop/jpackage/ +desktop/samples/ diff --git a/CHANGES.md b/CHANGES.md index 452cf004..d42756e8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,24 @@ +### Skin Composer Version 28 ### +* BREAKING CHANGE: Bitmap/Freetype Font settings files may not be compatible with this version of Skin Composer unless converted to UTF-8. + +* Skin Composer now has a wiki: https://github.com/raeleus/skin-composer/wiki +* Resolved release issues with Mac and Windows. +* Dialogs now highlight which field needs to be completed in order to continue. +* Added option to not show warnings when exporting a skin. +* Added option to import/export colors in a skin JSON as hexadecimal. +* Default export path is now a per project setting to prevent unfortunate accidents. +* Allow loading characters from a text file (UTF-8) in Font dialogs. This allows for any Unicode characters to be included in your fonts. +* Added detail view to Drawables dialog. +* Improved Image Font dialog's calculation of default baseline. +* Fixed pressing enter while typing in the preview dismisses the Image Font dialog. +* Fixed Create Bitmap Font dialog not closing file stream. +* Fixed errors when creating new font that overrides existing drawables. +* Fixed errors when opening a project with missing assets on Mac. +* Fixed unable to import FreeType fonts when importing a skin. +* Fixed filter error in native dialogs on Mac. +* Changes to texture packer settings no longer influence internal rendering of assets. This prevents "useIndexes" from crashing skin composer. +* Minor bug fixes and interface improvements. + ### Skin Composer Version 27 ### * Added filter option for Drawables dialog. Click the filter button or type text directly in the dialog. * Allow drag and drop of folders into Drawables and Fonts dialogs. diff --git a/README.md b/README.md index abf50cb3..9b4fcab8 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ### ![Logo](docs/images/logo.png) ### * Use Skin Composer to create skins for LibGDX's Scene2d.UI! -* Version 27 +* Version 28 * Live preview of all widgets with configurable options * Specify tinted, tiled, and custom created Drawables right in the editor * Includes BitmapFont editor and Image font generator like Shoebox @@ -24,14 +24,8 @@ See more examples and sample code at [Ray3k](https://ray3k.wordpress.com/artwork ### Notes ### -To run Skin Composer on OSX, use the executable Mac release. If that fails, download the skin_composer_mac.jar version of the app and run it via terminal with the following command: **java -XstartOnFirstThread -jar skin_composer_mac.jar** - -To run/build the project in Idea/Android Studio, do the following: - -* In 'File/Project Structure...' set the project language level to 10 -* Update run configuration with the following VM Option: -XstartOnFirstThread - -Thanks to contributer RaimundWege for this information. +Skin Composer has now has wiki: [start here](https://github.com/raeleus/skin-composer/wiki) to begin learning! +To run Skin Composer on OSX, please see the [wiki](https://github.com/raeleus/skin-composer/wiki/Getting-Started-With-Mac "Getting Started With Mac") for details. ### License ### MIT License diff --git a/build.gradle b/build.gradle index 48c9f2e8..90c0fee5 100644 --- a/build.gradle +++ b/build.gradle @@ -12,7 +12,7 @@ allprojects { apply plugin: "eclipse" apply plugin: "idea" - version = '27' + version = '28' ext { appName = "SkinComposer" gdxVersion = '1.9.10-SNAPSHOT' diff --git a/core/assets/defaults.json b/core/assets/atlas-export-settings.json similarity index 94% rename from core/assets/defaults.json rename to core/assets/atlas-export-settings.json index c2b6248a..77f33d58 100644 --- a/core/assets/defaults.json +++ b/core/assets/atlas-export-settings.json @@ -8,8 +8,8 @@ rotation: false, minWidth: 16, minHeight: 16, - maxWidth: 1024, - maxHeight: 1024, + maxWidth: 2048, + maxHeight: 2048, square: true, stripWhitespaceX: false, stripWhitespaceY: false, diff --git a/core/assets/atlas-internal-settings.json b/core/assets/atlas-internal-settings.json new file mode 100644 index 00000000..77f33d58 --- /dev/null +++ b/core/assets/atlas-internal-settings.json @@ -0,0 +1,37 @@ +{ + pot: true, + paddingX: 2, + paddingY: 2, + bleed: true, + edgePadding: true, + duplicatePadding: true, + rotation: false, + minWidth: 16, + minHeight: 16, + maxWidth: 2048, + maxHeight: 2048, + square: true, + stripWhitespaceX: false, + stripWhitespaceY: false, + alphaThreshold: 0, + filterMin: Linear, + filterMag: Linear, + wrapX: ClampToEdge, + wrapY: ClampToEdge, + format: RGBA8888, + alias: true, + outputFormat: png, + jpegQuality: 0.9, + ignoreBlankImages: true, + fast: true, + debug: false, + combineSubdirectories: false, + flattenPaths: true, + silent: true, + premultiplyAlpha: false, + useIndexes: false, + limitMemory: true, + grid: false, + scale: [ 1 ], + scaleSuffix: [ "" ] +} \ No newline at end of file diff --git a/core/assets/skin-composer-ui/MPLUS1p-Bold.ttf b/core/assets/skin-composer-ui/MPLUS1p-Bold.ttf new file mode 100644 index 00000000..79f62d2d Binary files /dev/null and b/core/assets/skin-composer-ui/MPLUS1p-Bold.ttf differ diff --git a/core/assets/skin-composer-ui/SawarabiGothic-Regular.ttf b/core/assets/skin-composer-ui/SawarabiGothic-Regular.ttf new file mode 100644 index 00000000..276c38ba Binary files /dev/null and b/core/assets/skin-composer-ui/SawarabiGothic-Regular.ttf differ diff --git a/core/assets/skin-composer-ui/skin-composer-ui.atlas b/core/assets/skin-composer-ui/skin-composer-ui.atlas index ca8001c5..13b82ca6 100644 --- a/core/assets/skin-composer-ui/skin-composer-ui.atlas +++ b/core/assets/skin-composer-ui/skin-composer-ui.atlas @@ -1,12 +1,12 @@ skin-composer-ui.png -size: 1024,1024 +size: 2048,2048 format: RGBA8888 filter: Linear,Linear repeat: none browse-field-over rotate: false - xy: 590, 330 + xy: 724, 1935 size: 27, 27 split: 2, 2, 2, 2 orig: 27, 27 @@ -14,7 +14,7 @@ browse-field-over index: -1 browse-field-pressed rotate: false - xy: 619, 330 + xy: 2000, 1927 size: 27, 27 split: 2, 2, 2, 2 orig: 27, 27 @@ -22,7 +22,7 @@ browse-field-pressed index: -1 button rotate: false - xy: 994, 996 + xy: 541, 1341 size: 27, 27 split: 5, 5, 4, 4 pad: 5, 5, 2, 2 @@ -31,28 +31,28 @@ button index: -1 button-close rotate: false - xy: 208, 269 + xy: 38, 1 size: 30, 21 orig: 30, 21 offset: 0, 0 index: -1 button-close-over rotate: false - xy: 756, 642 + xy: 70, 1 size: 30, 21 orig: 30, 21 offset: 0, 0 index: -1 button-close-pressed rotate: false - xy: 756, 619 + xy: 102, 1 size: 30, 21 orig: 30, 21 offset: 0, 0 index: -1 button-color-base rotate: false - xy: 648, 325 + xy: 570, 1331 size: 27, 27 split: 5, 5, 5, 5 pad: 3, 3, 3, 3 @@ -61,7 +61,7 @@ button-color-base index: -1 button-color-base-over rotate: false - xy: 265, 319 + xy: 243, 1268 size: 27, 27 split: 5, 5, 5, 5 pad: 3, 3, 3, 3 @@ -70,7 +70,7 @@ button-color-base-over index: -1 button-color-base-pressed rotate: false - xy: 294, 313 + xy: 243, 1239 size: 27, 27 split: 5, 5, 5, 5 pad: 3, 3, 3, 3 @@ -79,126 +79,126 @@ button-color-base-pressed index: -1 button-colorwheel rotate: false - xy: 883, 826 + xy: 1858, 1511 size: 22, 24 orig: 22, 24 offset: 0, 0 index: -1 button-colorwheel-over rotate: false - xy: 270, 293 + xy: 1882, 1508 size: 22, 24 orig: 22, 24 offset: 0, 0 index: -1 button-colorwheel-pressed rotate: false - xy: 294, 287 + xy: 234, 17 size: 22, 24 orig: 22, 24 offset: 0, 0 index: -1 button-delete rotate: false - xy: 720, 733 + xy: 571, 1433 size: 34, 35 orig: 34, 35 offset: 0, 0 index: -1 button-delete-disabled rotate: false - xy: 720, 696 + xy: 571, 1396 size: 34, 35 orig: 34, 35 offset: 0, 0 index: -1 button-delete-over rotate: false - xy: 720, 660 + xy: 1934, 1877 size: 34, 34 orig: 34, 34 offset: 0, 0 index: -1 button-delete-pressed rotate: false - xy: 720, 624 + xy: 1934, 1841 size: 34, 34 orig: 34, 34 offset: 0, 0 index: -1 button-delete-small rotate: false - xy: 231, 34 + xy: 2018, 1986 size: 25, 25 orig: 25, 25 offset: 0, 0 index: -1 button-delete-small-over rotate: false - xy: 231, 7 + xy: 243, 477 size: 25, 25 orig: 25, 25 offset: 0, 0 index: -1 button-delete-small-pressed rotate: false - xy: 756, 569 + xy: 243, 450 size: 25, 25 orig: 25, 25 offset: 0, 0 index: -1 button-download rotate: false - xy: 757, 801 + xy: 1970, 1847 size: 34, 34 orig: 34, 34 offset: 0, 0 index: -1 button-download-over rotate: false - xy: 793, 803 + xy: 1970, 1811 size: 34, 34 orig: 34, 34 offset: 0, 0 index: -1 button-download-pressed rotate: false - xy: 793, 839 + xy: 1970, 1883 size: 34, 34 orig: 34, 34 offset: 0, 0 index: -1 button-duplicate rotate: false - xy: 720, 588 + xy: 1934, 1805 size: 34, 34 orig: 34, 34 offset: 0, 0 index: -1 button-duplicate-disabled rotate: false - xy: 720, 770 + xy: 571, 1470 size: 34, 34 orig: 34, 34 offset: 0, 0 index: -1 button-duplicate-over rotate: false - xy: 716, 552 + xy: 1934, 1769 size: 34, 34 orig: 34, 34 offset: 0, 0 index: -1 button-duplicate-pressed rotate: false - xy: 716, 516 + xy: 1934, 1733 size: 34, 34 orig: 34, 34 offset: 0, 0 index: -1 button-file rotate: false - xy: 323, 313 + xy: 243, 1210 size: 27, 27 split: 1, 1, 1, 1 pad: 5, 5, 2, 2 @@ -207,7 +207,7 @@ button-file index: -1 button-file-over rotate: false - xy: 352, 313 + xy: 243, 1181 size: 27, 27 split: 1, 1, 1, 1 pad: 5, 5, 2, 2 @@ -216,7 +216,7 @@ button-file-over index: -1 button-file-pressed rotate: false - xy: 381, 313 + xy: 243, 1152 size: 27, 27 split: 1, 1, 1, 1 pad: 5, 5, 2, 2 @@ -225,7 +225,7 @@ button-file-pressed index: -1 button-menu-pressed rotate: false - xy: 381, 313 + xy: 243, 1152 size: 27, 27 split: 1, 1, 1, 1 pad: 5, 5, 2, 2 @@ -234,49 +234,49 @@ button-menu-pressed index: -1 button-filter rotate: false - xy: 858, 799 + xy: 1967, 1570 size: 21, 26 orig: 21, 26 offset: 0, 0 index: -1 button-filter-over rotate: false - xy: 881, 798 + xy: 1995, 1647 size: 21, 26 orig: 21, 26 offset: 0, 0 index: -1 button-filter-pressed rotate: false - xy: 908, 936 + xy: 599, 1332 size: 21, 26 orig: 21, 26 offset: 0, 0 index: -1 button-maximize rotate: false - xy: 756, 596 + xy: 1882, 1534 size: 30, 21 orig: 30, 21 offset: 0, 0 index: -1 button-maximize-over rotate: false - xy: 302, 342 + xy: 1934, 1638 size: 30, 21 orig: 30, 21 offset: 0, 0 index: -1 button-maximize-pressed rotate: false - xy: 334, 342 + xy: 302, 1330 size: 30, 21 orig: 30, 21 offset: 0, 0 index: -1 button-menu rotate: false - xy: 410, 313 + xy: 243, 1123 size: 27, 27 split: 1, 1, 1, 1 pad: 5, 5, 2, 2 @@ -285,7 +285,7 @@ button-menu index: -1 button-menu-over rotate: false - xy: 439, 313 + xy: 243, 1094 size: 27, 27 split: 1, 1, 1, 1 pad: 5, 5, 2, 2 @@ -294,49 +294,49 @@ button-menu-over index: -1 button-minimize rotate: false - xy: 366, 342 + xy: 349, 1347 size: 30, 21 orig: 30, 21 offset: 0, 0 index: -1 button-minimize-over rotate: false - xy: 398, 342 + xy: 381, 1347 size: 30, 21 orig: 30, 21 offset: 0, 0 index: -1 button-minimize-over copy rotate: false - xy: 430, 342 + xy: 413, 1347 size: 30, 21 orig: 30, 21 offset: 0, 0 index: -1 button-new rotate: false - xy: 716, 480 + xy: 1934, 1697 size: 34, 34 orig: 34, 34 offset: 0, 0 index: -1 button-new-over rotate: false - xy: 652, 354 + xy: 1934, 1661 size: 34, 34 orig: 34, 34 offset: 0, 0 index: -1 button-new-pressed rotate: false - xy: 757, 911 + xy: 607, 1474 size: 34, 34 orig: 34, 34 offset: 0, 0 index: -1 button-over rotate: false - xy: 468, 313 + xy: 243, 1065 size: 27, 27 split: 5, 5, 5, 4 pad: 5, 5, 2, 2 @@ -345,7 +345,7 @@ button-over index: -1 button-plus rotate: false - xy: 497, 313 + xy: 243, 1036 size: 27, 27 split: 5, 15, 4, 19 pad: 5, 19, 2, 2 @@ -354,7 +354,7 @@ button-plus index: -1 button-plus-over rotate: false - xy: 526, 313 + xy: 243, 1007 size: 27, 27 split: 5, 15, 5, 19 pad: 5, 19, 3, 3 @@ -363,7 +363,7 @@ button-plus-over index: -1 button-plus-pressed rotate: false - xy: 555, 300 + xy: 243, 978 size: 27, 27 split: 5, 15, 4, 19 pad: 5, 19, 2, 2 @@ -372,7 +372,7 @@ button-plus-pressed index: -1 button-pressed rotate: false - xy: 829, 935 + xy: 243, 949 size: 27, 27 split: 5, 5, 5, 5 pad: 5, 5, 2, 2 @@ -381,329 +381,329 @@ button-pressed index: -1 button-restore rotate: false - xy: 462, 342 + xy: 445, 1347 size: 30, 21 orig: 30, 21 offset: 0, 0 index: -1 button-restore-over rotate: false - xy: 494, 342 + xy: 477, 1347 size: 30, 21 orig: 30, 21 offset: 0, 0 index: -1 button-restore-pressed rotate: false - xy: 526, 342 + xy: 509, 1347 size: 30, 21 orig: 30, 21 offset: 0, 0 index: -1 button-settings rotate: false - xy: 757, 874 + xy: 607, 1437 size: 34, 35 orig: 34, 35 offset: 0, 0 index: -1 button-settings-disabled rotate: false - xy: 757, 837 + xy: 607, 1400 size: 34, 35 orig: 34, 35 offset: 0, 0 index: -1 button-settings-over rotate: false - xy: 793, 911 + xy: 571, 1360 size: 34, 34 orig: 34, 34 offset: 0, 0 index: -1 button-settings-pressed rotate: false - xy: 793, 875 + xy: 607, 1364 size: 34, 34 orig: 34, 34 offset: 0, 0 index: -1 button-settings-small rotate: false - xy: 318, 287 + xy: 265, 1310 size: 22, 24 orig: 22, 24 offset: 0, 0 index: -1 button-settings-small-over rotate: false - xy: 342, 287 + xy: 1919, 1612 size: 22, 24 orig: 22, 24 offset: 0, 0 index: -1 button-settings-small-pressed rotate: false - xy: 366, 287 + xy: 1919, 1586 size: 22, 24 orig: 22, 24 offset: 0, 0 index: -1 button-spinner-minus-h rotate: false - xy: 752, 540 + xy: 243, 395 size: 23, 27 orig: 23, 27 offset: 0, 0 index: -1 button-spinner-minus-h-over rotate: false - xy: 752, 511 + xy: 243, 366 size: 23, 27 orig: 23, 27 offset: 0, 0 index: -1 button-spinner-minus-h-over-reversed rotate: false - xy: 883, 935 + xy: 2006, 1844 size: 23, 27 orig: 23, 27 offset: 0, 0 index: -1 button-spinner-minus-h-pressed rotate: false - xy: 752, 482 + xy: 243, 337 size: 23, 27 orig: 23, 27 offset: 0, 0 index: -1 button-spinner-minus-h-pressed-reversed rotate: false - xy: 858, 881 + xy: 2006, 1815 size: 23, 27 orig: 23, 27 offset: 0, 0 index: -1 button-spinner-minus-h-reversed rotate: false - xy: 883, 906 + xy: 1970, 1646 size: 23, 27 orig: 23, 27 offset: 0, 0 index: -1 button-spinner-minus-v rotate: false - xy: 829, 910 + xy: 243, 924 size: 27, 23 orig: 27, 23 offset: 0, 0 index: -1 button-spinner-minus-v-over rotate: false - xy: 829, 885 + xy: 243, 899 size: 27, 23 orig: 27, 23 offset: 0, 0 index: -1 button-spinner-minus-v-over-reversed rotate: false - xy: 231, 186 + xy: 243, 629 size: 27, 23 orig: 27, 23 offset: 0, 0 index: -1 button-spinner-minus-v-pressed rotate: false - xy: 829, 860 + xy: 243, 874 size: 27, 23 orig: 27, 23 offset: 0, 0 index: -1 button-spinner-minus-v-pressed-reversed rotate: false - xy: 231, 161 + xy: 243, 604 size: 27, 23 orig: 27, 23 offset: 0, 0 index: -1 button-spinner-minus-v-reversed rotate: false - xy: 231, 136 + xy: 243, 579 size: 27, 23 orig: 27, 23 offset: 0, 0 index: -1 button-spinner-plus-h rotate: false - xy: 994, 967 + xy: 243, 308 size: 23, 27 orig: 23, 27 offset: 0, 0 index: -1 button-spinner-plus-h-over rotate: false - xy: 584, 300 + xy: 243, 279 size: 23, 27 orig: 23, 27 offset: 0, 0 index: -1 button-spinner-plus-h-over-reversed rotate: false - xy: 858, 852 + xy: 134, 10 size: 23, 27 orig: 23, 27 offset: 0, 0 index: -1 button-spinner-plus-h-pressed rotate: false - xy: 609, 301 + xy: 243, 250 size: 23, 27 orig: 23, 27 offset: 0, 0 index: -1 button-spinner-plus-h-pressed-reversed rotate: false - xy: 858, 910 + xy: 2006, 1873 size: 23, 27 orig: 23, 27 offset: 0, 0 index: -1 button-spinner-plus-h-reversed rotate: false - xy: 883, 877 + xy: 159, 10 size: 23, 27 orig: 23, 27 offset: 0, 0 index: -1 button-spinner-plus-v rotate: false - xy: 829, 835 + xy: 243, 849 size: 27, 23 orig: 27, 23 offset: 0, 0 index: -1 button-spinner-plus-v-over rotate: false - xy: 829, 810 + xy: 243, 824 size: 27, 23 orig: 27, 23 offset: 0, 0 index: -1 button-spinner-plus-v-over-reveresed rotate: false - xy: 231, 111 + xy: 243, 554 size: 27, 23 orig: 27, 23 offset: 0, 0 index: -1 button-spinner-plus-v-pressed rotate: false - xy: 709, 425 + xy: 243, 799 size: 27, 23 orig: 27, 23 offset: 0, 0 index: -1 button-spinner-plus-v-pressed-reversed rotate: false - xy: 231, 86 + xy: 243, 529 size: 27, 23 orig: 27, 23 offset: 0, 0 index: -1 button-spinner-plus-v-reversed rotate: false - xy: 231, 61 + xy: 243, 504 size: 27, 23 orig: 27, 23 offset: 0, 0 index: -1 button-swatches rotate: false - xy: 390, 287 + xy: 1919, 1560 size: 22, 24 orig: 22, 24 offset: 0, 0 index: -1 button-swatches-over rotate: false - xy: 414, 287 + xy: 1943, 1612 size: 22, 24 orig: 22, 24 offset: 0, 0 index: -1 button-swatches-pressed rotate: false - xy: 438, 287 + xy: 1943, 1586 size: 22, 24 orig: 22, 24 offset: 0, 0 index: -1 button-tiles rotate: false - xy: 486, 289 + xy: 1967, 1622 size: 22, 22 orig: 22, 22 offset: 0, 0 index: -1 button-tiles-over rotate: false - xy: 510, 289 + xy: 1967, 1598 size: 22, 22 orig: 22, 22 offset: 0, 0 index: -1 button-tiles-pressed rotate: false - xy: 462, 289 + xy: 1943, 1562 size: 22, 22 orig: 22, 22 offset: 0, 0 index: -1 checkbox-off rotate: false - xy: 918, 972 + xy: 640, 1510 size: 19, 15 orig: 19, 15 offset: 0, 0 index: -1 checkbox-off-over rotate: false - xy: 534, 296 + xy: 1516, 1498 size: 19, 15 orig: 19, 15 offset: 0, 0 index: -1 checkbox-off-pressed rotate: false - xy: 292, 270 + xy: 1537, 1498 size: 19, 15 orig: 19, 15 offset: 0, 0 index: -1 checkbox-on rotate: false - xy: 313, 270 + xy: 1558, 1498 size: 19, 15 orig: 19, 15 offset: 0, 0 index: -1 checkbox-on-over rotate: false - xy: 334, 270 + xy: 1579, 1498 size: 19, 15 orig: 19, 15 offset: 0, 0 index: -1 checkbox-on-pressed rotate: false - xy: 355, 270 + xy: 1600, 1498 size: 19, 15 orig: 19, 15 offset: 0, 0 index: -1 class-bar rotate: false - xy: 918, 964 + xy: 724, 1913 size: 7, 6 split: 0, 0, 0, 1 pad: 2, 2, 2, 3 @@ -712,14 +712,14 @@ class-bar index: -1 code-sample rotate: false - xy: 1, 482 + xy: 1, 1506 size: 600, 541 orig: 600, 541 offset: 0, 0 index: -1 color-box rotate: false - xy: 462, 275 + xy: 334, 1339 size: 12, 12 split: 2, 2, 2, 2 orig: 12, 12 @@ -727,56 +727,56 @@ color-box index: -1 color-picker rotate: false - xy: 968, 972 + xy: 243, 424 size: 24, 24 orig: 24, 24 offset: 0, 0 index: -1 color-scale rotate: false - xy: 240, 269 + xy: 116, 24 size: 16, 13 orig: 16, 13 offset: 0, 0 index: -1 color-scale-flipped rotate: false - xy: 689, 392 + xy: 184, 1 size: 16, 13 orig: 16, 13 offset: 0, 0 index: -1 cursor_resize_horizontal rotate: false - xy: 756, 665 + xy: 1970, 1675 size: 32, 32 orig: 32, 32 offset: 0, 0 index: -1 cursor_resize_ne rotate: false - xy: 756, 767 + xy: 1970, 1777 size: 32, 32 orig: 32, 32 offset: 0, 0 index: -1 cursor_resize_nw rotate: false - xy: 756, 733 + xy: 1970, 1743 size: 32, 32 orig: 32, 32 offset: 0, 0 index: -1 cursor_resize_vertical rotate: false - xy: 756, 699 + xy: 1970, 1709 size: 32, 32 orig: 32, 32 offset: 0, 0 index: -1 custom-drawable-skincomposer-image rotate: false - xy: 868, 989 + xy: 1963, 2013 size: 69, 34 split: 4, 4, 4, 4 pad: 0, 0, 0, 0 @@ -785,112 +785,112 @@ custom-drawable-skincomposer-image index: -1 font-title-export rotate: false - xy: 1, 268 + xy: 1, 1292 size: 205, 212 orig: 205, 212 offset: 0, 0 index: -1 icon-bg-color rotate: false - xy: 858, 939 + xy: 2006, 1902 size: 23, 23 orig: 23, 23 offset: 0, 0 index: -1 icon-bg-color-over rotate: false - xy: 893, 964 + xy: 228, 43 size: 23, 23 orig: 23, 23 offset: 0, 0 index: -1 icon-colorwheel rotate: false - xy: 738, 425 + xy: 243, 225 size: 23, 23 orig: 23, 23 offset: 0, 0 index: -1 icon-colorwheel-over rotate: false - xy: 736, 400 + xy: 243, 200 size: 23, 23 orig: 23, 23 offset: 0, 0 index: -1 icon-grid-dark rotate: false - xy: 270, 271 + xy: 1494, 1493 size: 20, 20 orig: 20, 20 offset: 0, 0 index: -1 icon-grid-light rotate: false - xy: 908, 856 + xy: 1472, 1493 size: 20, 20 orig: 20, 20 offset: 0, 0 index: -1 icon-portrait rotate: false - xy: 245, 284 + xy: 243, 175 size: 23, 23 orig: 23, 23 offset: 0, 0 index: -1 icon-portrait-over rotate: false - xy: 634, 300 + xy: 243, 150 size: 23, 23 orig: 23, 23 offset: 0, 0 index: -1 icon-resize rotate: false - xy: 908, 900 + xy: 622, 1342 size: 20, 20 orig: 20, 20 offset: 0, 0 index: -1 icon-resize-over rotate: false - xy: 908, 878 + xy: 1450, 1493 size: 20, 20 orig: 20, 20 offset: 0, 0 index: -1 icon-style rotate: false - xy: 883, 852 + xy: 209, 16 size: 23, 23 orig: 23, 23 offset: 0, 0 index: -1 icon-style-over rotate: false - xy: 858, 827 + xy: 184, 16 size: 23, 23 orig: 23, 23 offset: 0, 0 index: -1 icon-text rotate: false - xy: 659, 300 + xy: 243, 125 size: 23, 23 orig: 23, 23 offset: 0, 0 index: -1 icon-text-over rotate: false - xy: 684, 300 + xy: 243, 100 size: 23, 23 orig: 23, 23 offset: 0, 0 index: -1 link rotate: false - xy: 752, 580 + xy: 1966, 1653 size: 2, 6 split: 0, 0, 0, 1 pad: 0, 0, 0, 2 @@ -899,7 +899,7 @@ link index: -1 link-empty rotate: false - xy: 752, 572 + xy: 2045, 2005 size: 2, 6 split: 0, 0, 0, 1 pad: 0, 0, 0, 2 @@ -908,72 +908,436 @@ link-empty index: -1 list rotate: false - xy: 414, 271 + xy: 234, 1 size: 14, 14 split: 2, 2, 2, 2 pad: 3, 3, 3, 3 orig: 14, 14 offset: 0, 0 index: -1 -loading_0 +loading-animation rotate: false - xy: 1, 151 - size: 115, 115 - orig: 115, 115 + xy: 1, 1156 + size: 119, 134 + orig: 119, 134 offset: 0, 0 - index: -1 -loading_1 + index: 0 +loading-animation rotate: false - xy: 208, 365 - size: 115, 115 - orig: 115, 115 + xy: 208, 1370 + size: 119, 134 + orig: 119, 134 offset: 0, 0 - index: -1 -loading_2 + index: 1 +loading-animation rotate: false - xy: 603, 823 - size: 115, 115 - orig: 115, 115 + xy: 603, 1828 + size: 119, 134 + orig: 119, 134 offset: 0, 0 - index: -1 -loading_3 + index: 2 +loading-animation rotate: false - xy: 1, 34 - size: 115, 115 - orig: 115, 115 + xy: 1, 1020 + size: 119, 134 + orig: 119, 134 offset: 0, 0 - index: -1 -loading_4 + index: 3 +loading-animation rotate: false - xy: 325, 365 - size: 115, 115 - orig: 115, 115 + xy: 329, 1370 + size: 119, 134 + orig: 119, 134 offset: 0, 0 - index: -1 -loading_5 + index: 4 +loading-animation rotate: false - xy: 603, 706 - size: 115, 115 - orig: 115, 115 + xy: 603, 1692 + size: 119, 134 + orig: 119, 134 offset: 0, 0 - index: -1 -loading_6 + index: 5 +loading-animation rotate: false - xy: 442, 365 - size: 115, 115 - orig: 115, 115 + xy: 1, 884 + size: 119, 134 + orig: 119, 134 offset: 0, 0 - index: -1 -loading_7 + index: 6 +loading-animation rotate: false - xy: 603, 589 - size: 115, 115 - orig: 115, 115 + xy: 450, 1370 + size: 119, 134 + orig: 119, 134 offset: 0, 0 - index: -1 + index: 7 +loading-animation + rotate: false + xy: 603, 1556 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 8 +loading-animation + rotate: false + xy: 1, 748 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 9 +loading-animation + rotate: false + xy: 1, 612 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 10 +loading-animation + rotate: false + xy: 1, 476 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 11 +loading-animation + rotate: false + xy: 1, 340 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 12 +loading-animation + rotate: false + xy: 1, 204 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 13 +loading-animation + rotate: false + xy: 1, 68 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 14 +loading-animation + rotate: false + xy: 122, 1156 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 15 +loading-animation + rotate: false + xy: 122, 1020 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 16 +loading-animation + rotate: false + xy: 122, 884 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 17 +loading-animation + rotate: false + xy: 122, 748 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 18 +loading-animation + rotate: false + xy: 122, 612 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 19 +loading-animation + rotate: false + xy: 122, 476 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 20 +loading-animation + rotate: false + xy: 122, 340 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 21 +loading-animation + rotate: false + xy: 122, 204 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 22 +loading-animation + rotate: false + xy: 122, 68 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 23 +loading-animation + rotate: false + xy: 753, 1913 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 24 +loading-animation + rotate: false + xy: 874, 1913 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 25 +loading-animation + rotate: false + xy: 995, 1913 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 26 +loading-animation + rotate: false + xy: 1116, 1913 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 27 +loading-animation + rotate: false + xy: 1237, 1913 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 28 +loading-animation + rotate: false + xy: 1358, 1913 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 29 +loading-animation + rotate: false + xy: 1479, 1913 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 30 +loading-animation + rotate: false + xy: 1600, 1913 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 31 +loading-animation + rotate: false + xy: 1721, 1913 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 32 +loading-animation + rotate: false + xy: 1842, 1913 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 33 +loading-animation + rotate: false + xy: 724, 1777 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 34 +loading-animation + rotate: false + xy: 724, 1641 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 35 +loading-animation + rotate: false + xy: 845, 1777 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 36 +loading-animation + rotate: false + xy: 845, 1641 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 37 +loading-animation + rotate: false + xy: 966, 1777 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 38 +loading-animation + rotate: false + xy: 966, 1641 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 39 +loading-animation + rotate: false + xy: 1087, 1777 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 40 +loading-animation + rotate: false + xy: 1087, 1641 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 41 +loading-animation + rotate: false + xy: 1208, 1777 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 42 +loading-animation + rotate: false + xy: 1208, 1641 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 43 +loading-animation + rotate: false + xy: 1329, 1777 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 44 +loading-animation + rotate: false + xy: 1329, 1641 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 45 +loading-animation + rotate: false + xy: 1450, 1777 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 46 +loading-animation + rotate: false + xy: 1450, 1641 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 47 +loading-animation + rotate: false + xy: 1571, 1777 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 48 +loading-animation + rotate: false + xy: 1571, 1641 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 49 +loading-animation + rotate: false + xy: 1692, 1777 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 50 +loading-animation + rotate: false + xy: 1692, 1641 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 51 +loading-animation + rotate: false + xy: 1813, 1777 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 52 +loading-animation + rotate: false + xy: 1813, 1641 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 53 +loading-animation + rotate: false + xy: 724, 1505 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 54 +loading-animation + rotate: false + xy: 845, 1505 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 55 +loading-animation + rotate: false + xy: 966, 1505 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 56 +loading-animation + rotate: false + xy: 1087, 1505 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 57 +loading-animation + rotate: false + xy: 1208, 1505 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 58 +loading-animation + rotate: false + xy: 1329, 1505 + size: 119, 134 + orig: 119, 134 + offset: 0, 0 + index: 59 nine-patch-black rotate: false - xy: 82, 1 + xy: 25, 1 size: 4, 4 split: 1, 1, 1, 1 orig: 4, 4 @@ -981,7 +1345,7 @@ nine-patch-black index: -1 nine-patch-border rotate: false - xy: 397, 270 + xy: 2032, 1969 size: 15, 15 split: 1, 1, 1, 1 orig: 15, 15 @@ -989,7 +1353,7 @@ nine-patch-border index: -1 nine-patch-content-handle rotate: false - xy: 58, 1 + xy: 2000, 1921 size: 4, 4 split: 1, 1, 1, 1 orig: 4, 4 @@ -997,7 +1361,7 @@ nine-patch-content-handle index: -1 nine-patch-content-handle-over rotate: false - xy: 64, 1 + xy: 7, 1 size: 4, 4 split: 1, 1, 1, 1 orig: 4, 4 @@ -1005,7 +1369,7 @@ nine-patch-content-handle-over index: -1 nine-patch-content-handle-pressed rotate: false - xy: 70, 1 + xy: 13, 1 size: 4, 4 split: 1, 1, 1, 1 orig: 4, 4 @@ -1013,7 +1377,7 @@ nine-patch-content-handle-pressed index: -1 nine-patch-handle rotate: false - xy: 294, 342 + xy: 1963, 1913 size: 4, 4 split: 1, 1, 1, 1 orig: 4, 4 @@ -1021,7 +1385,7 @@ nine-patch-handle index: -1 nine-patch-handle-over rotate: false - xy: 76, 1 + xy: 19, 1 size: 4, 4 split: 1, 1, 1, 1 orig: 4, 4 @@ -1029,7 +1393,7 @@ nine-patch-handle-over index: -1 nine-patch-handle-pressed rotate: false - xy: 857, 964 + xy: 1, 1 size: 4, 4 split: 1, 1, 1, 1 orig: 4, 4 @@ -1037,35 +1401,35 @@ nine-patch-handle-pressed index: -1 nine-patch-switch-off rotate: false - xy: 652, 407 + xy: 173, 41 size: 53, 25 orig: 53, 25 offset: 0, 0 index: -1 nine-patch-switch-off-over rotate: false - xy: 208, 309 + xy: 208, 1297 size: 53, 25 orig: 53, 25 offset: 0, 0 index: -1 nine-patch-switch-on rotate: false - xy: 939, 998 + xy: 1963, 1986 size: 53, 25 orig: 53, 25 offset: 0, 0 index: -1 nine-patch-switch-on-over rotate: false - xy: 58, 7 + xy: 660, 1529 size: 53, 25 orig: 53, 25 offset: 0, 0 index: -1 resizer-background rotate: false - xy: 716, 450 + xy: 1826, 1507 size: 30, 28 split: 5, 5, 5, 5 orig: 30, 28 @@ -1073,49 +1437,49 @@ resizer-background index: -1 resizer-handle rotate: false - xy: 857, 970 + xy: 289, 1325 size: 9, 9 orig: 9, 9 offset: 0, 0 index: -1 resizer-handle-over rotate: false - xy: 258, 273 + xy: 2032, 1958 size: 9, 9 orig: 9, 9 offset: 0, 0 index: -1 resizer-handle-pressed rotate: false - xy: 518, 278 + xy: 1637, 1504 size: 9, 9 orig: 9, 9 offset: 0, 0 index: -1 resizer-minor-handle rotate: false - xy: 777, 511 + xy: 143, 1 size: 7, 7 orig: 7, 7 offset: 0, 0 index: -1 resizer-minor-handle-over rotate: false - xy: 777, 502 + xy: 715, 1529 size: 7, 7 orig: 7, 7 offset: 0, 0 index: -1 resizer-minor-handle-pressed rotate: false - xy: 777, 493 + xy: 152, 1 size: 7, 7 orig: 7, 7 offset: 0, 0 index: -1 resizer.foreground rotate: false - xy: 558, 329 + xy: 2000, 1956 size: 30, 28 split: 5, 5, 5, 5 orig: 30, 28 @@ -1123,21 +1487,21 @@ resizer.foreground index: -1 scrollpane rotate: false - xy: 430, 271 + xy: 250, 1 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 scrollpane-knob rotate: false - xy: 446, 271 + xy: 1621, 1499 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 selectbox rotate: false - xy: 1, 5 + xy: 603, 1527 size: 55, 27 split: 5, 23, 11, 11 pad: 3, 25, 3, 3 @@ -1146,7 +1510,7 @@ selectbox index: -1 selectbox-over rotate: false - xy: 652, 434 + xy: 116, 39 size: 55, 27 split: 5, 23, 11, 11 pad: 3, 25, 3, 3 @@ -1155,7 +1519,7 @@ selectbox-over index: -1 selectbox-pressed rotate: false - xy: 208, 336 + xy: 208, 1324 size: 55, 27 split: 5, 23, 11, 11 pad: 3, 25, 3, 3 @@ -1164,7 +1528,7 @@ selectbox-pressed index: -1 slider-horizontal rotate: false - xy: 908, 922 + xy: 724, 1921 size: 20, 12 split: 2, 2, 4, 4 pad: 0, 0, 0, 0 @@ -1173,28 +1537,28 @@ slider-horizontal index: -1 slider-knob rotate: false - xy: 476, 275 + xy: 2034, 2035 size: 12, 12 orig: 12, 12 offset: 0, 0 index: -1 slider-knob-over rotate: false - xy: 490, 275 + xy: 643, 1475 size: 12, 12 orig: 12, 12 offset: 0, 0 index: -1 slider-knob-pressed rotate: false - xy: 504, 275 + xy: 202, 2 size: 12, 12 orig: 12, 12 offset: 0, 0 index: -1 slider-vertical rotate: false - xy: 777, 547 + xy: 2034, 2013 size: 12, 20 split: 4, 4, 2, 2 pad: 0, 0, 0, 0 @@ -1203,7 +1567,7 @@ slider-vertical index: -1 slider-zoom rotate: false - xy: 753, 964 + xy: 208, 1353 size: 102, 15 split: 17, 0, 5, 5 orig: 102, 15 @@ -1211,28 +1575,28 @@ slider-zoom index: -1 slider-zoom-handle rotate: false - xy: 1019, 958 + xy: 661, 1510 size: 4, 17 orig: 4, 17 offset: 0, 0 index: -1 slider-zoom-handle-over rotate: false - xy: 596, 463 + xy: 1906, 1515 size: 4, 17 orig: 4, 17 offset: 0, 0 index: -1 slider-zoom-handle-pressed rotate: false - xy: 1019, 977 + xy: 2043, 1950 size: 4, 17 orig: 4, 17 offset: 0, 0 index: -1 splitpane rotate: false - xy: 927, 964 + xy: 733, 1913 size: 6, 6 split: 2, 2, 2, 2 pad: 0, 0, 0, 0 @@ -1241,14 +1605,14 @@ splitpane index: -1 status-bar rotate: false - xy: 376, 266 + xy: 643, 1489 size: 19, 19 orig: 19, 19 offset: 0, 0 index: -1 textfield rotate: false - xy: 707, 396 + xy: 243, 770 size: 27, 27 split: 1, 1, 1, 1 pad: 2, 2, 2, 2 @@ -1257,7 +1621,7 @@ textfield index: -1 browse-field rotate: false - xy: 707, 396 + xy: 243, 770 size: 27, 27 split: 1, 1, 1, 1 pad: 2, 2, 2, 2 @@ -1266,7 +1630,7 @@ browse-field index: -1 textfield-pressed rotate: false - xy: 677, 325 + xy: 243, 741 size: 27, 27 split: 1, 1, 1, 2 pad: 2, 2, 2, 2 @@ -1275,7 +1639,7 @@ textfield-pressed index: -1 textfield-spinner rotate: false - xy: 939, 969 + xy: 243, 712 size: 27, 27 split: 5, 5, 5, 5 pad: 3, 3, 3, 3 @@ -1284,7 +1648,7 @@ textfield-spinner index: -1 textfield-spinner-over rotate: false - xy: 231, 240 + xy: 243, 683 size: 27, 27 split: 5, 5, 5, 5 pad: 3, 3, 3, 3 @@ -1293,7 +1657,7 @@ textfield-spinner-over index: -1 textfield-spinner-pressed rotate: false - xy: 231, 211 + xy: 243, 654 size: 27, 27 split: 5, 5, 5, 5 pad: 3, 3, 3, 3 @@ -1302,91 +1666,91 @@ textfield-spinner-pressed index: -1 thumb-neon rotate: false - xy: 603, 463 + xy: 1450, 1515 size: 111, 124 orig: 111, 124 offset: 0, 0 index: -1 thumb-neutralizer rotate: false - xy: 118, 142 + xy: 1563, 1515 size: 111, 124 orig: 111, 124 offset: 0, 0 index: -1 thumb-plain-james rotate: false - xy: 118, 16 + xy: 1676, 1515 size: 111, 124 orig: 111, 124 offset: 0, 0 index: -1 thumb-scene2d rotate: false - xy: 753, 981 + xy: 1, 24 size: 113, 42 orig: 113, 42 offset: 0, 0 index: -1 thumb-video rotate: false - xy: 603, 940 + xy: 603, 1964 size: 148, 83 orig: 148, 83 offset: 0, 0 index: -1 thumb-vis-ui rotate: false - xy: 559, 359 + xy: 1789, 1537 size: 91, 102 orig: 91, 102 offset: 0, 0 index: -1 toggle-off rotate: false - xy: 753, 947 + xy: 1, 7 size: 35, 15 orig: 35, 15 offset: 0, 0 index: -1 toggle-off-over rotate: false - xy: 559, 465 + xy: 312, 1353 size: 35, 15 orig: 35, 15 offset: 0, 0 index: -1 toggle-off-pressed rotate: false - xy: 265, 348 + xy: 1789, 1520 size: 35, 15 orig: 35, 15 offset: 0, 0 index: -1 toggle-on rotate: false - xy: 652, 390 + xy: 1882, 1624 size: 35, 15 orig: 35, 15 offset: 0, 0 index: -1 toggle-on-over rotate: false - xy: 208, 292 + xy: 603, 1510 size: 35, 15 orig: 35, 15 offset: 0, 0 index: -1 toggle-on-pressed rotate: false - xy: 790, 947 + xy: 265, 1336 size: 35, 15 orig: 35, 15 offset: 0, 0 index: -1 welcome-frame rotate: false - xy: 777, 529 + xy: 134, 1 size: 7, 7 split: 2, 2, 2, 2 pad: 1, 1, 1, 1 @@ -1395,7 +1759,7 @@ welcome-frame index: -1 welcome-frame-over rotate: false - xy: 777, 538 + xy: 715, 1547 size: 7, 7 split: 2, 2, 2, 2 pad: 1, 1, 1, 1 @@ -1404,7 +1768,7 @@ welcome-frame-over index: -1 welcome-frame-pressed rotate: false - xy: 777, 520 + xy: 715, 1538 size: 7, 7 split: 2, 2, 2, 2 pad: 1, 1, 1, 1 @@ -1413,21 +1777,21 @@ welcome-frame-pressed index: -1 welcome-separator rotate: false - xy: 113, 31 + xy: 1919, 1638 size: 3, 1 orig: 3, 1 offset: 0, 0 index: -1 white rotate: false - xy: 555, 339 + xy: 1924, 1638 size: 1, 1 orig: 1, 1 offset: 0, 0 index: -1 window rotate: false - xy: 720, 873 + xy: 1963, 1919 size: 35, 65 split: 2, 2, 31, 4 pad: 1, 1, 31, 0 @@ -1436,7 +1800,7 @@ window index: -1 window-main rotate: false - xy: 720, 806 + xy: 1882, 1557 size: 35, 65 split: 1, 1, 31, 1 orig: 35, 65 @@ -1444,7 +1808,7 @@ window-main index: -1 window2 rotate: false - xy: 868, 964 + xy: 243, 75 size: 23, 23 split: 6, 6, 6, 6 pad: 2, 2, 2, 2 diff --git a/core/assets/skin-composer-ui/skin-composer-ui.json b/core/assets/skin-composer-ui/skin-composer-ui.json index 4301c223..3428beb3 100644 Binary files a/core/assets/skin-composer-ui/skin-composer-ui.json and b/core/assets/skin-composer-ui/skin-composer-ui.json differ diff --git a/core/assets/skin-composer-ui/skin-composer-ui.png b/core/assets/skin-composer-ui/skin-composer-ui.png index ab920ff9..40c6962c 100644 Binary files a/core/assets/skin-composer-ui/skin-composer-ui.png and b/core/assets/skin-composer-ui/skin-composer-ui.png differ diff --git a/core/assets/splash.png b/core/assets/splash.png index 2ce2d1b7..51c002bd 100644 Binary files a/core/assets/splash.png and b/core/assets/splash.png differ diff --git a/core/src/com/ray3k/skincomposer/AnimationDrawable.java b/core/src/com/ray3k/skincomposer/AnimationDrawable.java new file mode 100644 index 00000000..82107467 --- /dev/null +++ b/core/src/com/ray3k/skincomposer/AnimationDrawable.java @@ -0,0 +1,81 @@ +/* + * The MIT License + * + * Copyright 2018 Raymond Buckley. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.ray3k.skincomposer; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.graphics.g2d.Animation; +import com.badlogic.gdx.graphics.g2d.Animation.PlayMode; +import com.badlogic.gdx.graphics.g2d.Batch; +import com.badlogic.gdx.graphics.g2d.TextureRegion; +import com.badlogic.gdx.scenes.scene2d.ui.Skin; +import com.badlogic.gdx.scenes.scene2d.utils.BaseDrawable; + +public class AnimationDrawable extends BaseDrawable { + private String regionName; + private Skin skin; + + private Animation animation; + private float stateTime; + + public AnimationDrawable(Skin skin, String regionName, float frameDuration) { + super(); + stateTime = 0.0f; + + setRegion(skin, regionName, frameDuration); + } + + public void update(float delta) { + stateTime += delta; + } + + @Override + public void draw(Batch batch, float x, float y, float width, float height) { + batch.draw(animation.getKeyFrame(stateTime), x, y, width, height); + } + + public String getRegionName() { + return regionName; + } + + public Animation getAnimation() { + return animation; + } + + public void setRegion(Skin skin, String regionName, float frameDuration) { + this.skin = skin; + this.regionName = regionName; + animation = new Animation<>(frameDuration, skin.getRegions(regionName), PlayMode.LOOP); + setMinWidth(animation.getKeyFrame(0.0f).getRegionWidth()); + setMinHeight(animation.getKeyFrame(0.0f).getRegionHeight()); + } + + public float getFrameDuration() { + return animation.getFrameDuration(); + } + + public void setFrameDuration(float frameDuration) { + animation.setFrameDuration(frameDuration); + } +} diff --git a/core/src/com/ray3k/skincomposer/DesktopWorker.java b/core/src/com/ray3k/skincomposer/DesktopWorker.java index 2d5574f8..573f10b0 100644 --- a/core/src/com/ray3k/skincomposer/DesktopWorker.java +++ b/core/src/com/ray3k/skincomposer/DesktopWorker.java @@ -33,7 +33,7 @@ import java.util.List; public interface DesktopWorker { - public void texturePack(Array handles, FileHandle localFile, FileHandle targetFile); + public void texturePack(Array handles, FileHandle localFile, FileHandle targetFile, FileHandle settingsFile); public void packFontImages(Array files, FileHandle saveFile); public void sizeWindowToFit(int maxWidth, int maxHeight, int displayBorder, Graphics graphics); public void centerWindow(Graphics graphics); diff --git a/core/src/com/ray3k/skincomposer/Main.java b/core/src/com/ray3k/skincomposer/Main.java index 4e7bf116..6a1ebf36 100644 --- a/core/src/com/ray3k/skincomposer/Main.java +++ b/core/src/com/ray3k/skincomposer/Main.java @@ -75,7 +75,7 @@ import com.ray3k.skincomposer.utils.Utils; public class Main extends ApplicationAdapter { - public final static String VERSION = "27"; + public final static String VERSION = "28"; public static String newVersion; public static final Class[] BASIC_CLASSES = {Button.class, CheckBox.class, ImageButton.class, ImageTextButton.class, Label.class, List.class, @@ -93,7 +93,7 @@ public class Main extends ApplicationAdapter { private static Skin skin; private DialogFactory dialogFactory; private DesktopWorker desktopWorker; - private AnimatedDrawable loadingAnimation; + private AnimationDrawable loadingAnimation; private UndoableManager undoableManager; private ProjectData projectData; private RootTable rootTable; @@ -135,9 +135,9 @@ private void initDefaults() { skin.getFont("font").getData().markupEnabled = true; //copy defaults.json to temp folder if it doesn't exist - var fileHandle = appFolder.child("texturepacker/defaults.json"); + var fileHandle = appFolder.child("texturepacker/atlas-export-settings.json"); if (!fileHandle.exists()) { - Gdx.files.internal("defaults.json").copyTo(fileHandle); + Gdx.files.internal("atlas-export-settings.json").copyTo(fileHandle); } //copy preview fonts to preview fonts folder if they do not exist @@ -194,15 +194,7 @@ public void closed() { return false; }); - loadingAnimation = new AnimatedDrawable(.05f); - loadingAnimation.addDrawable(skin.getDrawable("loading_0")); - loadingAnimation.addDrawable(skin.getDrawable("loading_1")); - loadingAnimation.addDrawable(skin.getDrawable("loading_2")); - loadingAnimation.addDrawable(skin.getDrawable("loading_3")); - loadingAnimation.addDrawable(skin.getDrawable("loading_4")); - loadingAnimation.addDrawable(skin.getDrawable("loading_5")); - loadingAnimation.addDrawable(skin.getDrawable("loading_6")); - loadingAnimation.addDrawable(skin.getDrawable("loading_7")); + loadingAnimation = new AnimationDrawable(skin, "loading-animation", 1 / 30f); projectData.getAtlasData().clearTempData(); handListener = new HandListener(); @@ -271,7 +263,7 @@ public Stage getStage() { return stage; } - public AnimatedDrawable getLoadingAnimation() { + public AnimationDrawable getLoadingAnimation() { return loadingAnimation; } diff --git a/core/src/com/ray3k/skincomposer/MainListener.java b/core/src/com/ray3k/skincomposer/MainListener.java index f6b04e52..99b28e7e 100644 --- a/core/src/com/ray3k/skincomposer/MainListener.java +++ b/core/src/com/ray3k/skincomposer/MainListener.java @@ -289,7 +289,9 @@ public void newFile() { (int selection) -> { if (selection == 0) { saveFile(() -> { - projectData.clear(); + Gdx.app.postRunnable(() -> { + projectData.clear(); + }); }); } else if (selection == 1) { projectData.clear(); @@ -320,18 +322,20 @@ public void openFile() { filterPatterns = new String[] {"*.scmp"}; } - File file = desktopWorker.openDialog("Open skin file...", defaultPath, filterPatterns, "Skin Composer files"); + File file = desktopWorker.openDialog("Open Skin Composer file...", defaultPath, filterPatterns, "Skin Composer files"); if (file != null) { - FileHandle fileHandle = new FileHandle(file); - projectData.load(fileHandle); - Array drawableErrors = projectData.verifyDrawablePaths(); - Array fontErrors = projectData.verifyFontPaths(); - if (drawableErrors.size > 0 || fontErrors.size > 0) { - dialogFactory.showDialogPathErrors(drawableErrors, fontErrors); - } - projectData.setLastOpenSavePath(fileHandle.parent().path() + "/"); - root.populate(); - root.setRecentFilesDisabled(projectData.getRecentFiles().size == 0); + Gdx.app.postRunnable(() -> { + FileHandle fileHandle = new FileHandle(file); + projectData.load(fileHandle); + Array drawableErrors = projectData.verifyDrawablePaths(); + Array fontErrors = projectData.verifyFontPaths(); + if (drawableErrors.size > 0 || fontErrors.size > 0) { + dialogFactory.showDialogPathErrors(drawableErrors, fontErrors); + } + projectData.setLastOpenSavePath(fileHandle.parent().path() + "/"); + root.populate(); + root.setRecentFilesDisabled(projectData.getRecentFiles().size == 0); + }); } }; @@ -373,15 +377,17 @@ public void openFile(FileHandle fileHandle) { } if (fileHandle != null) { - projectData.load(fileHandle); - Array drawableErrors = projectData.verifyDrawablePaths(); - Array fontErrors = projectData.verifyFontPaths(); - if (drawableErrors.size > 0 || fontErrors.size > 0) { - dialogFactory.showDialogPathErrors(drawableErrors, fontErrors); - } - projectData.setLastOpenSavePath(fileHandle.parent().path() + "/"); - root.populate(); - root.setRecentFilesDisabled(projectData.getRecentFiles().size == 0); + Gdx.app.postRunnable(() -> { + projectData.load(fileHandle); + Array drawableErrors = projectData.verifyDrawablePaths(); + Array fontErrors = projectData.verifyFontPaths(); + if (drawableErrors.size > 0 || fontErrors.size > 0) { + dialogFactory.showDialogPathErrors(drawableErrors, fontErrors); + } + projectData.setLastOpenSavePath(fileHandle.parent().path() + "/"); + root.populate(); + root.setRecentFilesDisabled(projectData.getRecentFiles().size == 0); + }); } }; @@ -431,19 +437,24 @@ public void saveAsFile(Runnable runnable) { dialogFactory.showDialogLoading(() -> { String defaultPath = projectData.getLastOpenSavePath(); - String[] filterPatterns = {"*.scmp"}; + String[] filterPatterns = null; + if (!Utils.isMac()) { + filterPatterns = new String[] {"*.scmp"}; + } - File file = desktopWorker.saveDialog("Save skin file as...", defaultPath, filterPatterns, "Skin Composer files"); + File file = desktopWorker.saveDialog("Save Skin Composer file as...", defaultPath, filterPatterns, "Skin Composer files"); if (file != null) { - FileHandle fileHandle = new FileHandle(file); - if (fileHandle.extension() == null || !fileHandle.extension().equals(".scmp")) { - fileHandle = fileHandle.sibling(fileHandle.nameWithoutExtension() + ".scmp"); - } - projectData.save(fileHandle); - projectData.setLastOpenSavePath(fileHandle.parent().path() + "/"); - if (runnable != null) { - runnable.run(); - } + Gdx.app.postRunnable(() -> { + FileHandle fileHandle = new FileHandle(file); + if (fileHandle.extension() == null || !fileHandle.extension().equals(".scmp")) { + fileHandle = fileHandle.sibling(fileHandle.nameWithoutExtension() + ".scmp"); + } + projectData.save(fileHandle); + projectData.setLastOpenSavePath(fileHandle.parent().path() + "/"); + if (runnable != null) { + runnable.run(); + } + }); } }); } @@ -625,16 +636,18 @@ public void droppedScmpFile(FileHandle fileHandle) { public void refreshTextureAtlas() { main.getDialogFactory().showDialogLoading(() -> { - try { - main.getProjectData().getAtlasData().writeAtlas(); - main.getProjectData().getAtlasData().atlasCurrent = true; - main.getRootTable().produceAtlas(); - main.getRootTable().refreshPreview(); - } catch (Exception e) { - main.getDialogFactory().showDialogError("Error", "Unable to write texture atlas to temporary storage!", null); - Gdx.app.error(getClass().getName(), "Unable to write texture atlas to temporary storage!", e); - main.getDialogFactory().showDialogError("Atlas Error...", "Unable to write texture atlas to temporary storage.\n\nOpen log?"); - } + Gdx.app.postRunnable(() -> { + try { + main.getProjectData().getAtlasData().writeAtlas(Gdx.files.internal("atlas-internal-settings.json")); + main.getProjectData().getAtlasData().atlasCurrent = true; + main.getRootTable().produceAtlas(); + main.getRootTable().refreshPreview(); + } catch (Exception e) { + main.getDialogFactory().showDialogError("Error", "Unable to write texture atlas to temporary storage!", null); + Gdx.app.error(getClass().getName(), "Unable to write texture atlas to temporary storage!", e); + main.getDialogFactory().showDialogError("Atlas Error...", "Unable to write texture atlas to temporary storage.\n\nOpen log?"); + } + }); }); } diff --git a/core/src/com/ray3k/skincomposer/RootTable.java b/core/src/com/ray3k/skincomposer/RootTable.java index 5c7435ee..156a7827 100644 --- a/core/src/com/ray3k/skincomposer/RootTable.java +++ b/core/src/com/ray3k/skincomposer/RootTable.java @@ -633,6 +633,7 @@ private void addStyleProperties(final Table left) { parentSelectBox.setSelected(getSelectedStyle().parent); table.add(parentSelectBox); parentSelectBox.addListener(main.getHandListener()); + parentSelectBox.getList().addListener(main.getHandListener()); parentSelectBox.addListener(new StyleParentChangeListener(getSelectedStyle(), parentSelectBox)); //make preview respect parent @@ -2603,7 +2604,7 @@ public boolean produceAtlas() { } if (!main.getProjectData().getAtlasData().atlasCurrent) { - main.getProjectData().getAtlasData().writeAtlas(); + main.getProjectData().getAtlasData().writeAtlas(Gdx.files.internal("atlas-internal-settings.json")); main.getProjectData().getAtlasData().atlasCurrent = true; } atlas = main.getProjectData().getAtlasData().getAtlas(); diff --git a/core/src/com/ray3k/skincomposer/data/AtlasData.java b/core/src/com/ray3k/skincomposer/data/AtlasData.java index c22b2073..8c20c48e 100644 --- a/core/src/com/ray3k/skincomposer/data/AtlasData.java +++ b/core/src/com/ray3k/skincomposer/data/AtlasData.java @@ -23,7 +23,6 @@ ******************************************************************************/ package com.ray3k.skincomposer.data; -import com.badlogic.gdx.Gdx; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Pixmap; @@ -42,10 +41,12 @@ public class AtlasData implements Json.Serializable { public boolean atlasCurrent = false; private Array drawables; + private Array fontDrawables; private Main main; public AtlasData() { drawables = new Array<>(); + fontDrawables = new Array<>(); } public void setMain(Main main) { @@ -54,6 +55,7 @@ public void setMain(Main main) { public void clear() { drawables.clear(); + fontDrawables.clear(); atlasCurrent = false; } @@ -72,6 +74,22 @@ public DrawableData getDrawable(String name) { return returnValue; } + + public Array getFontDrawables() { + return fontDrawables; + } + + public DrawableData getFontDrawable(String name) { + DrawableData returnValue = null; + for (DrawableData data : fontDrawables) { + if (data.name.equals(name)) { + returnValue = data; + break; + } + } + + return returnValue; + } public void readAtlas(FileHandle fileHandle) throws Exception { if (fileHandle.exists()) { @@ -170,8 +188,12 @@ public void readAtlas(FileHandle fileHandle) throws Exception { } } - public void writeAtlas() throws Exception { - FileHandle targetFile = Main.appFolder.child("temp/" + main.getProjectData().getId() + ".atlas"); + public Array writeAtlas(FileHandle settingsFile) throws Exception { + return writeAtlas(Main.appFolder.child("temp/" + main.getProjectData().getId() + ".atlas"), settingsFile); + } + + public Array writeAtlas(FileHandle targetFile, FileHandle settingsFile) throws Exception { + Array warnings = new Array<>(); targetFile.parent().mkdirs(); FileHandle[] oldFiles = targetFile.parent().list(new FilenameFilter() { @Override @@ -185,30 +207,18 @@ public boolean accept(File file, String string) { targetFile.sibling(targetFile.nameWithoutExtension() + ".atlas").delete(); Array files = new Array<>(); - for (DrawableData drawable : drawables) { - if (!drawable.customized && !files.contains(drawable.file, false)) { + + + for (DrawableData drawable : fontDrawables) { + if (!files.contains(drawable.file, false)) { files.add(drawable.file); } - } - - main.getDesktopWorker().texturePack(files, main.getProjectData().getSaveFile(), targetFile); - } - - public Array writeAtlas(FileHandle targetFile) throws Exception { - Array warnings = new Array<>(); - targetFile.parent().mkdirs(); - FileHandle[] oldFiles = targetFile.parent().list(new FilenameFilter() { - @Override - public boolean accept(File file, String string) { - return string.matches(targetFile.nameWithoutExtension() + "\\d*\\.(?i)png"); + + if (!main.getProjectData().resourceExists(drawable.file)) { + warnings.add("[RED]ERROR:[] Drawable file [BLACK]" + drawable.file + "[] does not exist."); } - }); - for (FileHandle fileHandle : oldFiles) { - fileHandle.delete(); } - targetFile.sibling(targetFile.nameWithoutExtension() + ".atlas").delete(); - Array files = new Array<>(); for (DrawableData drawable : drawables) { if (!drawable.customized) { if (!files.contains(drawable.file, false)) { @@ -221,7 +231,7 @@ public boolean accept(File file, String string) { } } - main.getDesktopWorker().texturePack(files, main.getProjectData().getSaveFile(), targetFile); + main.getDesktopWorker().texturePack(files, main.getProjectData().getSaveFile(), targetFile, settingsFile); return warnings; } @@ -242,17 +252,22 @@ public void clearTempData() { public void set(AtlasData atlasData) { drawables.clear(); drawables.addAll(atlasData.drawables); + + fontDrawables.clear(); + fontDrawables.addAll(atlasData.fontDrawables); } @Override public void write(Json json) { json.writeValue("atlasCurrent", atlasCurrent); json.writeValue("drawables", drawables, Array.class, DrawableData.class); + json.writeValue("fontDrawables", fontDrawables, Array.class, DrawableData.class); } @Override public void read(Json json, JsonValue jsonData) { atlasCurrent = json.readValue("atlasCurrent", Boolean.TYPE, jsonData); drawables = json.readValue("drawables", Array.class, DrawableData.class, jsonData); + fontDrawables = json.readValue("fontDrawables", Array.class, DrawableData.class, new Array(),jsonData); } } \ No newline at end of file diff --git a/core/src/com/ray3k/skincomposer/data/DrawableData.java b/core/src/com/ray3k/skincomposer/data/DrawableData.java index a7207284..1fca499a 100644 --- a/core/src/com/ray3k/skincomposer/data/DrawableData.java +++ b/core/src/com/ray3k/skincomposer/data/DrawableData.java @@ -41,7 +41,6 @@ public static boolean validate(String name) { public FileHandle file; public Color bgColor; - public boolean visible; public Color tint; public String tintName; public String name; @@ -58,7 +57,6 @@ public DrawableData(FileHandle file) { } else { bgColor = Color.WHITE; } - visible = true; this.name = proper(file.name()); customized = false; } @@ -66,7 +64,6 @@ public DrawableData(FileHandle file) { public DrawableData(String customName) { name = customName; customized = true; - visible = true; bgColor = Color.WHITE; tiled = false; } @@ -102,7 +99,6 @@ public void write(Json json) { json.writeValue("file", (String) null); } json.writeValue("bgColor", bgColor); - json.writeValue("visible", visible); json.writeValue("tint", tint); json.writeValue("tintName", tintName); json.writeValue("name", name); @@ -118,7 +114,6 @@ public void read(Json json, JsonValue jsonData) { file = new FileHandle(jsonData.getString("file")); } bgColor = json.readValue("bgColor", Color.class, jsonData); - visible = json.readValue("visible", Boolean.TYPE, jsonData); tint = json.readValue("tint", Color.class, jsonData); tintName = json.readValue("tintName", String.class, jsonData); name = json.readValue("name", String.class, jsonData); diff --git a/core/src/com/ray3k/skincomposer/data/JsonData.java b/core/src/com/ray3k/skincomposer/data/JsonData.java index 9f016b7d..1154e483 100644 --- a/core/src/com/ray3k/skincomposer/data/JsonData.java +++ b/core/src/com/ray3k/skincomposer/data/JsonData.java @@ -27,6 +27,7 @@ import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.g2d.BitmapFont; +import com.badlogic.gdx.graphics.g2d.BitmapFont.BitmapFontData; import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator; import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.scenes.scene2d.ui.Label; @@ -105,7 +106,7 @@ public void clear() { } /** - * Imports skin data from a JSON file. Supports skins from LibGDX 1.9.9 + * Imports skin data from a JSON file. Supports skins from LibGDX 1.9.10 * @param fileHandle * @return * @throws Exception @@ -165,7 +166,11 @@ public Array readFile(FileHandle fileHandle) throws Exception { BitmapFont.BitmapFontData bitmapFontData = new BitmapFont.BitmapFontData(fontCopy, false); for (String path : bitmapFontData.imagePaths) { FileHandle file = new FileHandle(path); - main.getProjectData().getAtlasData().getDrawable(file.nameWithoutExtension()).visible = false; + + var drawable = main.getProjectData().getAtlasData().getDrawable(file.nameWithoutExtension()); + + main.getProjectData().getAtlasData().getDrawables().removeValue(drawable, false); + main.getProjectData().getAtlasData().getFontDrawables().add(drawable); } } } @@ -176,7 +181,7 @@ else if (child.name().equals(FreeTypeFontGenerator.class.getName())) { FreeTypeFontData data = new FreeTypeFontData(); data.name = font.name; data.previewTTF = font.getString("previewTTF", null); - data.useCustomSerializer= font.getBoolean("useCustomSerializer", false); + data.useCustomSerializer= font.getBoolean("useCustomSerializer", true); data.size = font.getInt("size", 16); data.mono = font.getBoolean("mono", false); data.hinting = font.getString("hinting", "AutoMedium"); @@ -233,7 +238,13 @@ else if (child.name().equals(FreeTypeFontGenerator.class.getName())) { } //colors else if (child.name().equals(Color.class.getName()) || child.name().equals(Color.class.getSimpleName())) { for (JsonValue color : child.iterator()) { - ColorData colorData = new ColorData(color.name, new Color(color.getFloat("r", 0.0f), color.getFloat("g", 0.0f), color.getFloat("b", 0.0f), color.getFloat("a", 0.0f))); + var colorValue = new Color(); + if (color.has("hex")) { + colorValue.set(Color.valueOf(color.getString("hex"))); + } else { + colorValue.set(color.getFloat("r", 1.0f), color.getFloat("g", 1.0f), color.getFloat("b", 1.0f), color.getFloat("a", 1.0f)); + } + ColorData colorData = new ColorData(color.name, colorValue); //delete colors with the same name for (ColorData originalData : new Array<>(colors)) { @@ -251,7 +262,6 @@ else if (child.name().equals(TiledDrawable.class.getName()) || child.name().equa drawableData.name = tiledDrawable.name; drawableData.tiled = true; - drawableData.visible = true; drawableData.tintName = tiledDrawable.getString("color"); drawableData.minWidth = tiledDrawable.getFloat("minWidth", 0.0f); drawableData.minHeight = tiledDrawable.getFloat("minHeight", 0.0f); @@ -272,7 +282,12 @@ else if (child.name().equals(TintedDrawable.class.getName()) || child.name().equ drawableData.name = tintedDrawable.name; if (!tintedDrawable.get("color").isString()) { - drawableData.tint = new Color(tintedDrawable.get("color").getFloat("r", 0.0f), tintedDrawable.get("color").getFloat("g", 0.0f), tintedDrawable.get("color").getFloat("b", 0.0f), tintedDrawable.get("color").getFloat("a", 0.0f)); + drawableData.tint = new Color(); + if (tintedDrawable.get("color").has("hex")) { + drawableData.tint.set(Color.valueOf(tintedDrawable.get("color").getString("hex"))); + } else { + drawableData.tint.set(tintedDrawable.get("color").getFloat("r", 1.0f), tintedDrawable.get("color").getFloat("g", 1.0f), tintedDrawable.get("color").getFloat("b", 1.0f), tintedDrawable.get("color").getFloat("a", 1.0f)); + } } else { drawableData.tintName = tintedDrawable.getString("color"); } @@ -576,11 +591,16 @@ public Array writeFile(FileHandle fileHandle) { json.writeObjectStart(className); for (ColorData color : colors) { json.writeObjectStart(color.getName()); - json.writeValue("r", color.color.r); - json.writeValue("g", color.color.g); - json.writeValue("b", color.color.b); - json.writeValue("a", color.color.a); - json.writeObjectEnd(); + if (main.getProjectData().isExportingHex()) { + json.writeValue("hex", color.color.toString()); + json.writeObjectEnd(); + } else { + json.writeValue("r", color.color.r); + json.writeValue("g", color.color.g); + json.writeValue("b", color.color.b); + json.writeValue("a", color.color.a); + json.writeObjectEnd(); + } } json.writeObjectEnd(); } @@ -647,10 +667,14 @@ public Array writeFile(FileHandle fileHandle) { json.writeValue("name", DrawableData.proper(drawable.file.name())); if (drawable.tint != null) { json.writeObjectStart("color"); - json.writeValue("r", drawable.tint.r); - json.writeValue("g", drawable.tint.g); - json.writeValue("b", drawable.tint.b); - json.writeValue("a", drawable.tint.a); + if (main.getProjectData().isExportingHex()) { + json.writeValue("hex", drawable.tint.toString()); + } else { + json.writeValue("r", drawable.tint.r); + json.writeValue("g", drawable.tint.g); + json.writeValue("b", drawable.tint.b); + json.writeValue("a", drawable.tint.a); + } json.writeObjectEnd(); } else if (drawable.tintName != null) { json.writeValue("color", drawable.tintName); @@ -978,6 +1002,28 @@ public void read(Json json, JsonValue jsonData) { main.getDialogFactory().showDialogError("Error while reading file...", "Error while attempting to read save file.\nPlease ensure that file is not corrupted.\n\nOpen error log?"); } } + + /** + * Moves font drawables to the appropriate list when reading from legacy save files. + */ + public void translateFontDrawables(AtlasData atlasData) { + + for (var font : fonts) { + if (font.file.exists()) { + var bitmapFontData = new BitmapFontData(font.file, false); + for (String path : bitmapFontData.imagePaths) { + FileHandle file = new FileHandle(path); + + var drawable = atlasData.getDrawable(file.nameWithoutExtension()); + + if (drawable != null) { + atlasData.getDrawables().removeValue(drawable, false); + atlasData.getFontDrawables().add(drawable); + } + } + } + } + } /** * Creates a new StyleData object if one with the same name currently does not exist. If it does exist diff --git a/core/src/com/ray3k/skincomposer/data/ProjectData.java b/core/src/com/ray3k/skincomposer/data/ProjectData.java index 1738a5e1..8343e0b7 100644 --- a/core/src/com/ray3k/skincomposer/data/ProjectData.java +++ b/core/src/com/ray3k/skincomposer/data/ProjectData.java @@ -163,6 +163,15 @@ public boolean isCheckingForUpdates() { return generalPref.getBoolean("checkForUpdates", true); } + public void setShowingExportWarnings(boolean allow) { + generalPref.putBoolean("exportWarnings", allow); + generalPref.flush(); + } + + public boolean isShowingExportWarnings() { + return generalPref.getBoolean("exportWarnings", true); + } + public void setExportFormat(ExportFormat exportFormat) { generalPref.putString("exportFormat", exportFormat.toString()); } @@ -236,6 +245,22 @@ else if (localImportFolder != null && !localImportFolder.equals(targetFolder) && } } + for (DrawableData drawableData : atlasData.getFontDrawables()) { + if (drawableData.file != null && drawableData.file.exists()) { + targetFolder.mkdirs(); + //drawable files in the temp folder + if (drawableData.file.parent().equals(tempImportFolder)) { + drawableData.file.moveTo(targetFolder); + drawableData.file = targetFolder.child(drawableData.file.name()); + } + //drawable files in the folder next to the old save + else if (localImportFolder != null && !localImportFolder.equals(targetFolder) && drawableData.file.parent().equals(localImportFolder)) { + drawableData.file.copyTo(targetFolder); + drawableData.file = targetFolder.child(drawableData.file.name()); + } + } + } + for (FontData fontData : jsonData.getFonts()) { if (fontData.file.exists()) { targetFolder.mkdirs(); @@ -282,6 +307,14 @@ public void makeResourcesRelative(FileHandle saveFile) { } } + for (DrawableData drawableData : main.getAtlasData().getFontDrawables()) { + if (drawableData.file.exists() && !targetFolder.equals(drawableData.file.parent())) { + targetFolder.mkdirs(); + drawableData.file.copyTo(targetFolder); + drawableData.file = targetFolder.child(drawableData.file.name()); + } + } + for (FontData fontData : main.getJsonData().getFonts()) { if (fontData.file.exists() && !targetFolder.equals(fontData.file.parent())) { fontData.file.copyTo(targetFolder); @@ -366,6 +399,12 @@ public Array verifyDrawablePaths() { errors.add(drawable); } } + + for (DrawableData drawable : atlasData.getFontDrawables()) { + if (!drawable.customized && (drawable.file == null || !drawable.file.exists())) { + errors.add(drawable); + } + } } else { FileHandle targetFolder = saveFile.sibling(saveFile.nameWithoutExtension() + "_data/"); @@ -381,6 +420,19 @@ public Array verifyDrawablePaths() { } } } + + for (DrawableData drawable : atlasData.getFontDrawables()) { + if (!drawable.customized) { + if (drawable.file == null) { + errors.add(drawable); + } else { + FileHandle localFile = targetFolder.child(drawable.file.name()); + if (!localFile.exists()) { + errors.add(drawable); + } + } + } + } } return errors; } @@ -426,6 +478,15 @@ private void correctFilePaths() { } } + for (DrawableData drawableData : atlasData.getFontDrawables()) { + if (resourcesRelative || drawableData.file != null && !drawableData.file.exists()) { + FileHandle newFile = targetFolder.child(drawableData.file.name()); + if (newFile.exists()) { + drawableData.file = newFile; + } + } + } + for (FontData fontData : jsonData.getFonts()) { if (resourcesRelative || !fontData.file.exists()) { FileHandle newFile = targetFolder.child(fontData.file.name()); @@ -476,6 +537,8 @@ public void read(Json json, JsonValue jsonValue) { preferences = json.readValue("preferences", ObjectMap.class, jsonValue); jsonData.set(json.readValue("jsonData", JsonData.class, jsonValue)); atlasData.set(json.readValue("atlasData", AtlasData.class, jsonValue)); + jsonData.translateFontDrawables(atlasData); + if (!jsonValue.get("saveFile").isNull()) { saveFile = new FileHandle(jsonValue.getString("saveFile")); } @@ -504,14 +567,11 @@ public void setLastOpenSavePath(String openSavePath) { } public String getLastImportExportPath() { - return (String) generalPref.getString("last-import-export-path", - generalPref.getString("last-path", - Gdx.files.getLocalStoragePath())); + return (String) preferences.get("last-import-export-path", Gdx.files.getLocalStoragePath()); } public void setLastImportExportPath(String importExportPath) { - generalPref.putString("last-import-export-path", importExportPath); - generalPref.flush(); + preferences.put("last-import-export-path", importExportPath); setLastPath(importExportPath); } @@ -584,6 +644,14 @@ public void setExportingFonts(boolean exportAtlas) { preferences.put("export-fonts", exportAtlas); } + public boolean isExportingHex() { + return (boolean) preferences.get("export-hex", false); + } + + public void setExportingHex(boolean exportHex) { + preferences.put("export-hex", exportHex); + } + /** * Returns true if file exists and depending on the state of relative resources * and save file state. diff --git a/core/src/com/ray3k/skincomposer/dialog/Dialog9Patch.java b/core/src/com/ray3k/skincomposer/dialog/Dialog9Patch.java index da5ec4af..7f1aeba2 100644 --- a/core/src/com/ray3k/skincomposer/dialog/Dialog9Patch.java +++ b/core/src/com/ray3k/skincomposer/dialog/Dialog9Patch.java @@ -23,6 +23,7 @@ */ package com.ray3k.skincomposer.dialog; +import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Input.Keys; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.graphics.Color; @@ -114,7 +115,9 @@ public Dialog9Patch(Main main) { filesDroppedListener = (Array files) -> { if (files.size > 0 && files.first().extension().equalsIgnoreCase("png")) { Runnable runnable = () -> { - loadImage(files.first()); + Gdx.app.postRunnable(() -> { + loadImage(files.first()); + }); }; main.getDialogFactory().showDialogLoading(runnable); @@ -738,6 +741,7 @@ public boolean scrolled(InputEvent event, float x, float y, int amount) { table.add(selectBox); selectBox.setItems("None", "Text", "Color", "Drawable"); selectBox.addListener(main.getHandListener()); + selectBox.getList().addListener(main.getHandListener()); selectBox.getList().addListener(new ClickListener() { @Override public void clicked(InputEvent event, float x, float y) { @@ -919,17 +923,19 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { File file = main.getDesktopWorker().saveDialog("Save nine patch file as...", defaultPath, filterPatterns, "Nine Patch files"); if (file != null) { - FileHandle fileHandle = new FileHandle(file); - if (fileHandle.extension() == null || !fileHandle.name().toLowerCase(Locale.ROOT).endsWith(".9.png")) { - fileHandle = fileHandle.sibling(fileHandle.nameWithoutExtension() + ".9.png"); - } - saveNinePatch(fileHandle); - main.getProjectData().setLastDrawablePath(fileHandle.parent().path() + "/"); - hide(); - - for (var listener : listeners) { - listener.fileSaved(fileHandle); - } + Gdx.app.postRunnable(() -> { + FileHandle fileHandle = new FileHandle(file); + if (fileHandle.extension() == null || !fileHandle.name().toLowerCase(Locale.ROOT).endsWith(".9.png")) { + fileHandle = fileHandle.sibling(fileHandle.nameWithoutExtension() + ".9.png"); + } + saveNinePatch(fileHandle); + main.getProjectData().setLastDrawablePath(fileHandle.parent().path() + "/"); + hide(); + + for (var listener : listeners) { + listener.fileSaved(fileHandle); + } + }); } }); } @@ -1008,8 +1014,10 @@ private void showLoadImageDialog() { File file = main.getDesktopWorker().openDialog("Open Image...", defaultPath, filterPatterns, "Image files"); if (file != null) { - var fileHandle = new FileHandle(file); - loadImage(fileHandle); + Gdx.app.postRunnable(() -> { + var fileHandle = new FileHandle(file); + loadImage(fileHandle); + }); } }; @@ -1415,8 +1423,10 @@ private void showLoadPatchesDialog() { File file = main.getDesktopWorker().openDialog("Load Patches from File...", defaultPath, filterPatterns, "Nine Patch files"); if (file != null) { - var fileHandle = new FileHandle(file); - loadPatches(fileHandle); + Gdx.app.postRunnable(() -> { + var fileHandle = new FileHandle(file); + loadPatches(fileHandle); + }); } }; @@ -1588,12 +1598,14 @@ private void showBatchApplyDialog() { var files = main.getDesktopWorker().openMultipleDialog("Batch apply to files", defaultPath, filterPatterns, "Image files"); if (files != null && files.size() > 0) { - var fileHandles = new Array(); - for (var file : files) { - var fileHandle = new FileHandle(file); - fileHandles.add(fileHandle); - } - batchApply(fileHandles); + Gdx.app.postRunnable(() -> { + var fileHandles = new Array(); + for (var file : files) { + var fileHandle = new FileHandle(file); + fileHandles.add(fileHandle); + } + batchApply(fileHandles); + }); } }; diff --git a/core/src/com/ray3k/skincomposer/dialog/DialogBitmapFont.java b/core/src/com/ray3k/skincomposer/dialog/DialogBitmapFont.java index 8a9f1c19..e433dcea 100644 --- a/core/src/com/ray3k/skincomposer/dialog/DialogBitmapFont.java +++ b/core/src/com/ray3k/skincomposer/dialog/DialogBitmapFont.java @@ -29,8 +29,10 @@ import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.scenes.scene2d.Actor; +import com.badlogic.gdx.scenes.scene2d.Group; import com.badlogic.gdx.scenes.scene2d.InputEvent; import com.badlogic.gdx.scenes.scene2d.ui.*; +import com.badlogic.gdx.scenes.scene2d.ui.Label.LabelStyle; import com.badlogic.gdx.scenes.scene2d.ui.TextField.TextFieldStyle; import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; import com.badlogic.gdx.scenes.scene2d.utils.ClickListener; @@ -114,7 +116,9 @@ public DialogBitmapFont(Main main) { var extension = files.first().extension().toLowerCase(Locale.ROOT); if (extension.equals("ttf")) { Runnable runnable = () -> { - loadTTFsource(files.first()); + Gdx.app.postRunnable(() -> { + loadTTFsource(files.first()); + }); }; main.getDialogFactory().showDialogLoading(runnable); @@ -161,7 +165,7 @@ public void addListener(DialogBitmapFontListener listener) { private void populate() { root.pad(15.0f); - Label label = new Label("Create a new Bitmap Font.", skin, "required"); + Label label = new Label("Create a new Bitmap Font.", skin, "black"); root.add(label); root.row(); @@ -227,6 +231,7 @@ public void selected(Color color) { table.defaults().space(5.0f); label = new Label("Source TTF Path:", skin); + label.setName("source-label"); table.add(label).right(); textField = new TextField(data.file == null ? "" : data.file.path(), skin); @@ -262,7 +267,9 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { File file = main.getDesktopWorker().openDialog("Select TTF file...", defaultPath, filterPatterns, "True Type Font files"); if (file != null) { - loadTTFsource(new FileHandle(file)); + Gdx.app.postRunnable(() -> { + loadTTFsource(new FileHandle(file)); + }); } }; @@ -275,6 +282,7 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { table.row(); table.defaults().space(5.0f); label = new Label("Target FNT Path:", skin); + label.setName("target-label"); table.add(label).right(); textField = new TextField(target == null ? "" : data.file.path(), skin); @@ -310,18 +318,20 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { File file = main.getDesktopWorker().saveDialog("Select FNT file...", defaultPath, filterPatterns, "Bitmap Font files"); if (file != null) { - target = new FileHandle(file); - if (!target.extension().equalsIgnoreCase("fnt")) { - target = target.sibling(target.name() + ".fnt"); - } + Gdx.app.postRunnable(() -> { + target = new FileHandle(file); + if (!target.extension().equalsIgnoreCase("fnt")) { + target = target.sibling(target.name() + ".fnt"); + } - var textField = (TextField) DialogBitmapFont.this.findActor("targetFileField"); - textField.setText(target.path()); - textField.setCursorPosition(textField.getText().length() - 1); + var textField = (TextField) DialogBitmapFont.this.findActor("targetFileField"); + textField.setText(target.path()); + textField.setCursorPosition(textField.getText().length() - 1); - main.getProjectData().setLastFontPath(target.parent().path() + "/"); + main.getProjectData().setLastFontPath(target.parent().path() + "/"); - updatePreviewAndOK(); + updatePreviewAndOK(); + }); } }; @@ -346,10 +356,12 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { charactersTextField.addListener(toolTip); var characterSelectBox = new SelectBox(skin); - characterSelectBox.setItems("default", "0-9", "a-zA-Z", "a-zA-Z0-9", "custom"); + characterSelectBox.setName("characterSelectBox"); + characterSelectBox.setItems("default", "0-9", "a-zA-Z", "a-zA-Z0-9", "custom", "Load from file (UTF-8)..."); table.add(characterSelectBox).fillX(); characterSelectBox.addListener(main.getHandListener()); + characterSelectBox.getList().addListener(main.getHandListener()); toolTip = new TextTooltip("Character preset list", main.getTooltipManager(), getSkin()); characterSelectBox.addListener(toolTip); characterSelectBox.addListener(new ChangeListener() { @@ -358,19 +370,32 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { switch (characterSelectBox.getSelected()) { case "default": charactersTextField.setText(""); + charactersTextField.setMessageText(""); + data.characters = ""; break; case "0-9": charactersTextField.setText("0123456789"); + charactersTextField.setMessageText(""); + data.characters = charactersTextField.getText(); break; case "a-zA-Z": charactersTextField.setText("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + charactersTextField.setMessageText(""); + data.characters = charactersTextField.getText(); break; case "a-zA-Z0-9": charactersTextField.setText("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"); + charactersTextField.setMessageText(""); + data.characters = charactersTextField.getText(); + break; + case "Load from file (UTF-8)...": + charactersTextField.setText(""); + charactersTextField.setMessageText("Characters loaded from text file..."); + data.characters = ""; + showCharacterDialog(); break; } - - data.characters = charactersTextField.getText(); + data.characters = !data.characters.equals("") && !data.characters.contains("\u0000") ? "\u0000" + data.characters : data.characters; updatePreviewAndOK(); } @@ -453,6 +478,7 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { selectBox.addListener(toolTip); selectBox.addListener(main.getHandListener()); + selectBox.getList().addListener(main.getHandListener()); selectBox.addListener(new ChangeListener() { @Override public void changed(ChangeListener.ChangeEvent event, Actor actor) { @@ -464,6 +490,7 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { }); label = new Label("Color:", skin); + label.setName("color-label"); bottom.add(label).right(); textButton = new TextButton(data.color, skin); @@ -570,6 +597,7 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { }); label = new Label("Border Color:", skin); + label.setName("border-color-label"); bottom.add(label).right(); textButton = new TextButton(data.borderColor, skin); @@ -698,6 +726,7 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { bottom.row(); label = new Label("Shadow Color:", skin); + label.setName("shadow-color-label"); bottom.add(label).right(); textButton = new TextButton(data.shadowColor, skin); @@ -883,6 +912,7 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { selectBox.addListener(toolTip); selectBox.addListener(main.getHandListener()); + selectBox.getList().addListener(main.getHandListener()); selectBox.addListener(new ChangeListener() { @Override public void changed(ChangeListener.ChangeEvent event, Actor actor) { @@ -906,6 +936,7 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { selectBox.addListener(toolTip); selectBox.addListener(main.getHandListener()); + selectBox.getList().addListener(main.getHandListener()); selectBox.addListener(new ChangeListener() { @Override public void changed(ChangeListener.ChangeEvent event, Actor actor) { @@ -949,6 +980,8 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { textButton.setName("cancelButton"); textButton.addListener(main.getHandListener()); button(textButton, ButtonType.CANCEL); + + updateLabelHighlight(null); } private void loadTTFsource(FileHandle file) { @@ -1002,14 +1035,21 @@ private void updatePreviewAndOK() { boolean notValid = false; if (data.color == null) { notValid = true; + updateLabelHighlight("color-label"); } else if (data.file == null || !data.file.exists()) { notValid = true; + updateLabelHighlight("source-label"); } else if (target == null) { notValid = true; + updateLabelHighlight("target-label"); } else if (!MathUtils.isZero(data.borderWidth) && data.borderColor == null) { notValid = true; + updateLabelHighlight("border-color-label"); } else if ((data.shadowOffsetX != 0 || data.shadowOffsetY != 0) && data.shadowColor == null) { notValid = true; + updateLabelHighlight("shadow-color-label"); + } else { + updateLabelHighlight(null); } if (notValid) { @@ -1068,13 +1108,15 @@ private void saveSettings() { File file = main.getDesktopWorker().saveDialog("Save Bitmap Font settings...", defaultPath, filterPatterns, "Font Settings files"); if (file != null) { - var fileHandle = new FileHandle(file); - - if (!fileHandle.extension().toLowerCase(Locale.ROOT).equals("scmp-font")) { - fileHandle = fileHandle.sibling(fileHandle.name() + ".scmp-font"); - } - - saveSettings(fileHandle); + Gdx.app.postRunnable(() -> { + var fileHandle = new FileHandle(file); + + if (!fileHandle.extension().toLowerCase(Locale.ROOT).equals("scmp-font")) { + fileHandle = fileHandle.sibling(fileHandle.name() + ".scmp-font"); + } + + saveSettings(fileHandle); + }); } }; @@ -1083,7 +1125,7 @@ private void saveSettings() { private void saveSettings(FileHandle fileHandle) { var fontSettings = new FontSettings(); - fontSettings.characters = ((TextField) findActor("characters")).getText(); + fontSettings.characters = data.characters; fontSettings.size = ((Spinner) findActor("size")).getValueAsInt(); fontSettings.mono = ((Button) findActor("mono")).isChecked(); fontSettings.hinting = ((SelectBox) findActor("hinting")).getSelected(); @@ -1121,7 +1163,7 @@ private void saveSettings(FileHandle fileHandle) { fontSettings.minFilter = ((SelectBox) findActor("minFilter")).getSelected(); fontSettings.magFilter = ((SelectBox) findActor("magFilter")).getSelected(); - fileHandle.writeString(json.prettyPrint(fontSettings), false); + fileHandle.writeString(json.prettyPrint(fontSettings), false, "utf-8"); } private static class FontSettings { @@ -1164,7 +1206,9 @@ private void loadSettings() { File file = main.getDesktopWorker().openDialog("Select Bitmap Font settings...", defaultPath, filterPatterns, "Font Settings files"); if (file != null) { - loadSettings(new FileHandle(file)); + Gdx.app.postRunnable(() -> { + loadSettings(new FileHandle(file)); + }); } }; @@ -1172,7 +1216,7 @@ private void loadSettings() { } private void loadSettings(FileHandle fileHandle) { - var fontSettings = json.fromJson(FontSettings.class, fileHandle); + var fontSettings = json.fromJson(FontSettings.class, fileHandle.readString("utf-8")); ((TextField) findActor("characters")).setText(fontSettings.characters); data.characters = fontSettings.characters; @@ -1276,4 +1320,54 @@ private void loadSettings(FileHandle fileHandle) { updateColors(); updatePreviewAndOK(); } -} + + private void updateLabelHighlight(String requiredLabelName) { + var normalStyle = skin.get(LabelStyle.class); + var requiredStyle = skin.get("required", LabelStyle.class); + var actors = new Array(); + actors.addAll(getChildren()); + + for (int i = 0; i < actors.size; i++) { + var actor = actors.get(i); + + if (actor instanceof Group) { + actors.addAll(((Group) actor).getChildren()); + } + + if (actor instanceof Label) { + Label label = (Label) actor; + + if (label.getStyle().equals(requiredStyle)) { + label.setStyle(normalStyle); + } + + if (requiredLabelName != null && label.getName() != null && label.getName().equals(requiredLabelName)) { + label.setStyle(requiredStyle); + } + } + } + } + + private void showCharacterDialog() { + Runnable runnable = () -> { + String defaultPath = main.getProjectData().getLastFontPath(); + + File file = main.getDesktopWorker().openDialog("Select character text file...", defaultPath, null, "All files"); + if (file != null) { + var fileHandle = new FileHandle(file); + String characters = fileHandle.readString("utf-8"); + Gdx.app.postRunnable(() -> { + data.characters = Utils.removeDuplicateCharacters(characters); + updatePreviewAndOK(); + }); + } else { + Gdx.app.postRunnable(() -> { + SelectBox selectBox = findActor("characterSelectBox"); + selectBox.setSelected("default"); + }); + } + }; + + main.getDialogFactory().showDialogLoading(runnable); + } +} \ No newline at end of file diff --git a/core/src/com/ray3k/skincomposer/dialog/DialogColorPicker.java b/core/src/com/ray3k/skincomposer/dialog/DialogColorPicker.java index ad9823bd..5f50d714 100644 --- a/core/src/com/ray3k/skincomposer/dialog/DialogColorPicker.java +++ b/core/src/com/ray3k/skincomposer/dialog/DialogColorPicker.java @@ -608,53 +608,53 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { ((Table) t.findActor("confirmTable")).add(table).colspan(2); table.defaults().space(10.0f); - label = new Label("#", skin, "required"); + label = new Label("#", skin, "black"); table.add(label); table.add(hexField).width(75.0f); t.row(); table = new Table(); - label = new Label("R", skin, "required"); + label = new Label("R", skin, "black"); table.add(label); table.add(redSpinner).padLeft(10.0f).minWidth(90.0f); t.add(table); table = new Table(); - label = new Label("H", skin, "required"); + label = new Label("H", skin, "black"); table.add(label); table.add(hueSpinner).padLeft(10.0f).minWidth(90.0f); t.add(table); t.row(); table = new Table(); - label = new Label("G", skin, "required"); + label = new Label("G", skin, "black"); table.add(label); table.add(greenSpinner).padLeft(10.0f).minWidth(90.0f); t.add(table); table = new Table(); - label = new Label("S", skin, "required"); + label = new Label("S", skin, "black"); table.add(label); table.add(saturationSpinner).padLeft(10.0f).minWidth(90.0f); t.add(table); t.row(); table = new Table(); - label = new Label("B", skin, "required"); + label = new Label("B", skin, "black"); table.add(label); table.add(blueSpinner).padLeft(10.0f).minWidth(90.0f); t.add(table); table = new Table(); - label = new Label("B", skin, "required"); + label = new Label("B", skin, "black"); table.add(label); table.add(brightnessSpinner).padLeft(10.0f).minWidth(90.0f); t.add(table); t.row(); table = new Table(); - label = new Label("A", skin, "required"); + label = new Label("A", skin, "black"); table.add(label); t.add(table); table.add(alphaSpinner).padLeft(10.0f).minWidth(90.0f); diff --git a/core/src/com/ray3k/skincomposer/dialog/DialogColors.java b/core/src/com/ray3k/skincomposer/dialog/DialogColors.java index 96ae4450..cd2f9263 100644 --- a/core/src/com/ray3k/skincomposer/dialog/DialogColors.java +++ b/core/src/com/ray3k/skincomposer/dialog/DialogColors.java @@ -446,7 +446,7 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { colorTable.row(); } } else { - colorTable.add(new Label("No colors have been set!", getSkin(), "required")); + colorTable.add(new Label("No colors have been set!", getSkin(), "black")); } } diff --git a/core/src/com/ray3k/skincomposer/dialog/DialogCustomClass.java b/core/src/com/ray3k/skincomposer/dialog/DialogCustomClass.java index a32d56ba..2f00c50c 100644 --- a/core/src/com/ray3k/skincomposer/dialog/DialogCustomClass.java +++ b/core/src/com/ray3k/skincomposer/dialog/DialogCustomClass.java @@ -131,6 +131,7 @@ public void next(boolean up) { checkBox.setChecked(declareAfterUIclasses); checkBox.setName("declareAfterUIcheckBox"); getContentTable().add(checkBox).padLeft(10.0f).padRight(10.0f).left().padTop(10.0f); + checkBox.addListener(main.getHandListener()); getButtonTable().defaults().padBottom(10.0f).minWidth(50.0f); button("OK", true); diff --git a/core/src/com/ray3k/skincomposer/dialog/DialogDrawables.java b/core/src/com/ray3k/skincomposer/dialog/DialogDrawables.java index 7ff7336f..fc9ed7b1 100644 --- a/core/src/com/ray3k/skincomposer/dialog/DialogDrawables.java +++ b/core/src/com/ray3k/skincomposer/dialog/DialogDrawables.java @@ -34,6 +34,7 @@ import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.scenes.scene2d.Action; import com.badlogic.gdx.scenes.scene2d.Actor; +import com.badlogic.gdx.scenes.scene2d.Group; import com.badlogic.gdx.scenes.scene2d.InputEvent; import com.badlogic.gdx.scenes.scene2d.InputListener; import com.badlogic.gdx.scenes.scene2d.Stage; @@ -84,7 +85,7 @@ public class DialogDrawables extends Dialog { public static DialogDrawables instance; - private final static int[] sizes = {125, 150, 200, 250}; + private final static int[] sizes = {40, 125, 150, 200, 250}; private static float scrollPosition = 0.0f; private static int sortSelection = 0; private SelectBox sortSelectBox; @@ -95,7 +96,7 @@ public class DialogDrawables extends Dialog { private Array drawables; private ObjectMap drawablePairs; private TextureAtlas atlas; - private HorizontalGroup contentGroup; + private Table contentTable; private FilesDroppedListener filesDroppedListener; private DialogDrawablesListener listener; private Main main; @@ -186,11 +187,6 @@ public void initialize(Main main, DialogDrawablesListener listener) { */ private void gatherDrawables() { drawables = new Array<>(main.getAtlasData().getDrawables()); - Iterator iter = drawables.iterator(); - while(iter.hasNext()) { - DrawableData drawable = iter.next(); - if (!drawable.visible) iter.remove(); - } } /** @@ -205,7 +201,7 @@ private boolean produceAtlas() { atlas = null; } if (!main.getAtlasData().atlasCurrent) { - main.getAtlasData().writeAtlas(); + main.getAtlasData().writeAtlas(Gdx.files.internal("atlas-internal-settings.json")); main.getAtlasData().atlasCurrent = true; } atlas = main.getAtlasData().getAtlas(); @@ -366,7 +362,8 @@ public void cancelled() { } table.add(new Label("Zoom:", getSkin())).right().expandX(); - zoomSlider = new Slider(0, 3, 1, false, getSkin()); + zoomSlider = new Slider(0, 4, 1, false, getSkin()); + zoomSlider.setValue(1); zoomSlider.addListener(new ChangeListener() { @Override public void changed(ChangeListener.ChangeEvent event, Actor actor) { @@ -377,9 +374,8 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { table.add(zoomSlider); getContentTable().row(); - contentGroup = new HorizontalGroup(); - contentGroup.center().wrap(true).space(5.0f).wrapSpace(5.0f).rowAlign(Align.left); - scrollPane = new ScrollPane(contentGroup, getSkin()); + contentTable = new Table(); + scrollPane = new ScrollPane(contentTable, getSkin()); scrollPane.setFadeScrollBars(false); scrollPane.setFlickScroll(false); getContentTable().add(scrollPane).grow(); @@ -409,17 +405,245 @@ public Dialog show(Stage stage, Action action) { } private void refreshDrawableDisplay() { - contentGroup.clear(); + contentTable.clear(); if (drawables.size == 0) { Label label = new Label("No drawables have been added!", getSkin()); if (filterOptions.applied) { label.setText("No drawables match filter!"); } - contentGroup.addActor(label); + contentTable.add(label); + } else { + if (MathUtils.isZero(zoomSlider.getValue())) { + refreshDrawableDisplayDetail(); + } else { + refreshDrawableDisplayNormal(); + } } + } + + private void refreshDrawableDisplayDetail() { + contentTable.pad(5); + contentTable.defaults().space(3); + for (var drawable: drawables) { + Button drawableButton; + + if (property != null || customProperty != null) { + drawableButton = new Button(getSkin(), "color-base"); + drawableButton.addListener(new ChangeListener() { + @Override + public void changed(ChangeListener.ChangeEvent event, Actor actor) { + result(drawable); + hide(); + } + }); + drawableButton.addListener(main.getHandListener()); + } else { + drawableButton = new Button(getSkin(), "color-base-static"); + } + contentTable.add(drawableButton).growX(); + contentTable.row(); + + Table table = new Table(); + drawableButton.add(table).growX(); + table.defaults().minWidth(25); + + ClickListener fixDuplicateTouchListener = new ClickListener() { + @Override + public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) { + event.setBubbles(false); + return super.touchDown(event, x, y, pointer, button); + } + }; + + //preview + Container bg = new Container(); + bg.setClip(true); + bg.setBackground(getSkin().getDrawable("white")); + bg.setColor(drawable.bgColor); + + Image image = new Image(drawablePairs.get(drawable)); + if (MathUtils.isEqual(zoomSlider.getValue(), 1)) { + image.setScaling(Scaling.fit); + bg.fill(false); + } else { + image.setScaling(Scaling.stretch); + bg.fill(); + } + bg.setActor(image); + table.add(bg).size(sizes[MathUtils.floor(zoomSlider.getValue())]); + + //color wheel + if (!drawable.customized && !drawable.tiled && drawable.tint == null && drawable.tintName == null) { + Button button = new Button(getSkin(), "colorwheel"); + button.addListener(new ChangeListener() { + @Override + public void changed(ChangeListener.ChangeEvent event, Actor actor) { + newTintedDrawable(drawable); + event.setBubbles(false); + } + }); + button.addListener(fixDuplicateTouchListener); + if (property == null && customProperty == null) { + button.addListener(main.getHandListener()); + } + table.add(button); + + var toolTip = new TextTooltip("New Tinted Drawable", main.getTooltipManager(), getSkin()); + button.addListener(toolTip); + } + + //swatches + if (!drawable.customized && !drawable.tiled && drawable.tint == null && drawable.tintName == null) { + Button button = new Button(getSkin(), "swatches"); + button.addListener(new ChangeListener() { + @Override + public void changed(ChangeListener.ChangeEvent event, Actor actor) { + colorSwatchesDialog(drawable); + event.setBubbles(false); + } + }); + button.addListener(fixDuplicateTouchListener); + if (property == null && customProperty == null) { + button.addListener(main.getHandListener()); + } + table.add(button); + + var toolTip = new TextTooltip("Tinted Drawable from Colors", main.getTooltipManager(), getSkin()); + button.addListener(toolTip); + } + + //tiles button + if (!drawable.customized && !drawable.tiled && drawable.tint == null && drawable.tintName == null) { + Button button = new Button(getSkin(), "tiles"); + button.addListener(new ChangeListener() { + @Override + public void changed(ChangeListener.ChangeEvent event, + Actor actor) { + DrawableData tiledDrawable = new DrawableData(); + tiledDrawable.name = drawable.name; + tiledDrawable.file = drawable.file; + tiledDrawable.tiled = true; + Vector2 dimensions = Utils.imageDimensions(drawable.file); + tiledDrawable.minWidth = dimensions.x; + tiledDrawable.minHeight = dimensions.y; + tiledDrawableSettingsDialog("New Tiled Drawable", tiledDrawable, true); + event.setBubbles(false); + } + }); + button.addListener(fixDuplicateTouchListener); + if (property == null && customProperty == null) { + button.addListener(main.getHandListener()); + } + table.add(button); + + var toolTip = new TextTooltip("Tiled Drawable", main.getTooltipManager(), getSkin()); + button.addListener(toolTip); + } + + //tiled settings + if (drawable.tiled) { + Button button = new Button(getSkin(), "settings-small"); + button.addListener(new ChangeListener() { + @Override + public void changed(ChangeListener.ChangeEvent event, Actor actor) { + tiledDrawableSettingsDialog("Tiled Drawable Settings", drawable, false); + event.setBubbles(false); + } + }); + button.addListener(fixDuplicateTouchListener); + if (property == null && customProperty == null) { + button.addListener(main.getHandListener()); + } + table.add(); + table.add(); + table.add(button); + + var toolTip = new TextTooltip("Tiled Drawable Settings", main.getTooltipManager(), getSkin()); + button.addListener(toolTip); + } + + //rename (ONLY FOR TINTS) + else if (drawable.tint != null || drawable.tintName != null) { + Button button = new Button(getSkin(), "settings-small"); + button.addListener(new ChangeListener() { + @Override + public void changed(ChangeListener.ChangeEvent event, Actor actor) { + renameDrawableDialog(drawable); + event.setBubbles(false); + } + }); + button.addListener(fixDuplicateTouchListener); + if (property == null && customProperty == null) { + button.addListener(main.getHandListener()); + } + table.add(); + table.add(); + table.add(button); + + var toolTip = new TextTooltip("Rename Tinted Drawable", main.getTooltipManager(), getSkin()); + button.addListener(toolTip); + } + + //settings for custom drawables + else if (drawable.customized) { + Button button = new Button(getSkin(), "settings-small"); + button.addListener(new ChangeListener() { + @Override + public void changed(ChangeListener.ChangeEvent event, Actor actor) { + renameCustomDrawableDialog(drawable); + event.setBubbles(false); + } + }); + button.addListener(fixDuplicateTouchListener); + if (property == null && customProperty == null) { + button.addListener(main.getHandListener()); + } + table.add(); + table.add(); + table.add(button); + + var toolTip = new TextTooltip("Rename Custom Drawable", main.getTooltipManager(), getSkin()); + button.addListener(toolTip); + } + + //delete + Button button = new Button(getSkin(), "delete-small"); + button.addListener(new ChangeListener() { + @Override + public void changed(ChangeListener.ChangeEvent event, Actor actor) { + deleteDrawable(drawable); + event.setBubbles(false); + } + }); + button.addListener(fixDuplicateTouchListener); + if (property == null && customProperty == null) { + button.addListener(main.getHandListener()); + } + table.add(button); + + var toolTip = new TextTooltip("Delete Drawable", main.getTooltipManager(), getSkin()); + button.addListener(toolTip); + + //name + Label label = new Label(drawable.name, getSkin()); + label.setAlignment(Align.left); + label.setEllipsis("..."); + label.setEllipsis(true); + table.add(label).growX(); + + //Tooltip + toolTip = new TextTooltip(drawable.name, main.getTooltipManager(), getSkin()); + label.addListener(toolTip); + } + } + + private void refreshDrawableDisplayNormal() { + var contentGroup = new HorizontalGroup(); + contentGroup.center().wrap(true).space(5.0f).wrapSpace(5.0f).rowAlign(Align.left); + contentTable.add(contentGroup).grow(); - for (DrawableData drawable : drawables) { + for (var drawable : drawables) { Button drawableButton; if (property != null || customProperty != null) { @@ -449,7 +673,7 @@ public boolean touchDown(InputEvent event, float x, float y, int pointer, int bu }; //color wheel - if (!drawable.customized && !drawable.tiled) { + if (!drawable.customized && !drawable.tiled && drawable.tint == null && drawable.tintName == null) { Button button = new Button(getSkin(), "colorwheel"); button.addListener(new ChangeListener() { @Override @@ -471,7 +695,7 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { } //swatches - if (!drawable.customized && !drawable.tiled) { + if (!drawable.customized && !drawable.tiled && drawable.tint == null && drawable.tintName == null) { Button button = new Button(getSkin(), "swatches"); button.addListener(new ChangeListener() { @Override @@ -492,8 +716,8 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { table.add(); } - //tiles button (NOT FOR TINTS OR CUSTOM DRAWABLES) - if (!drawable.customized && drawable.tint == null && drawable.tintName == null) {; + //tiles button + if (!drawable.customized && !drawable.tiled && drawable.tint == null && drawable.tintName == null) { Button button = new Button(getSkin(), "tiles"); button.addListener(new ChangeListener() { @Override @@ -503,11 +727,10 @@ public void changed(ChangeListener.ChangeEvent event, tiledDrawable.name = drawable.name; tiledDrawable.file = drawable.file; tiledDrawable.tiled = true; - tiledDrawable.visible = true; Vector2 dimensions = Utils.imageDimensions(drawable.file); tiledDrawable.minWidth = dimensions.x; tiledDrawable.minHeight = dimensions.y; - tiledDrawableSettingsDialog("New Tiled Drawable", tiledDrawable); + tiledDrawableSettingsDialog("New Tiled Drawable", tiledDrawable, true); event.setBubbles(false); } }); @@ -529,7 +752,7 @@ public void changed(ChangeListener.ChangeEvent event, button.addListener(new ChangeListener() { @Override public void changed(ChangeListener.ChangeEvent event, Actor actor) { - tiledDrawableSettingsDialog("Tiled Drawable Settings", drawable); + tiledDrawableSettingsDialog("Tiled Drawable Settings", drawable, false); event.setBubbles(false); } }); @@ -609,7 +832,7 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { bg.setColor(drawable.bgColor); Image image = new Image(drawablePairs.get(drawable)); - if (MathUtils.isZero(zoomSlider.getValue())) { + if (MathUtils.isEqual(zoomSlider.getValue(), 1)) { image.setScaling(Scaling.fit); bg.fill(false); } else { @@ -774,26 +997,15 @@ public Dialog show(Stage stage) { textField.addListener(new ChangeListener() { @Override public void changed(ChangeListener.ChangeEvent event, Actor actor) { - boolean disable = !DrawableData.validate(textField.getText()); - if (!disable) { - for (DrawableData data : main.getAtlasData().getDrawables()) { - if (data.name.equals(textField.getText())) { - disable = true; - break; - } - } - } + boolean disable = !DrawableData.validate(textField.getText()) || checkIfNameExists(textField.getText()); okButton.setDisabled(disable); } }); - textField.setTextFieldListener(new TextField.TextFieldListener() { - @Override - public void keyTyped(TextField textField, char c) { - if (c == '\n') { - if (!okButton.isDisabled()) { - renameDrawable(drawable, textField.getText()); - dialog.hide(); - } + textField.setTextFieldListener((TextField textField1, char c) -> { + if (c == '\n') { + if (!okButton.isDisabled()) { + renameDrawable(drawable, textField1.getText()); + dialog.hide(); } } }); @@ -821,7 +1033,7 @@ private void renameDrawable(DrawableData drawable, String name) { sortBySelectedMode(); } - private void tiledDrawableSettingsDialog(String title, DrawableData drawable) { + private void tiledDrawableSettingsDialog(String title, DrawableData drawable, boolean newDrawable) { final Spinner minWidthSpinner = new Spinner(0.0f, 1.0f, true, Spinner.Orientation.HORIZONTAL, getSkin()); final Spinner minHeightSpinner = new Spinner(0.0f, 1.0f, true, Spinner.Orientation.HORIZONTAL, getSkin()); TextField textField = new TextField("", getSkin()) { @@ -861,7 +1073,9 @@ public Dialog show(Stage stage) { tileDialog.getContentTable().padLeft(10.0f).padRight(10.0f).padTop(5.0f); tileDialog.getButtonTable().padBottom(15.0f); - tileDialog.getContentTable().add(new Label("Please enter a name for the TiledDrawable: ", getSkin())); + var label = new Label("Please enter a name for the TiledDrawable: ", getSkin()); + label.setName("name-label"); + tileDialog.getContentTable().add(label); tileDialog.button("OK", true); tileDialog.button("Cancel", false).key(Keys.ESCAPE, false); @@ -879,7 +1093,8 @@ public Dialog show(Stage stage) { table.add(textField).growX().colspan(2); table.row(); - var label = new Label("Color:", getSkin()); + label = new Label("Color:", getSkin()); + label.setName("color-label"); table.add(label).right(); var subTable = new Table(); @@ -913,7 +1128,7 @@ public void changed(ChangeListener.ChangeEvent ce, Actor actor) { button.setColor(Color.WHITE); } } - okButton.setDisabled(!validateTiledDrawable(drawable, textField.getText(), (ColorData) button.getUserObject())); + okButton.setDisabled(!validateTiledDrawable(tileDialog, textField.getText(), drawable.name, (ColorData) button.getUserObject(), newDrawable)); }); dialog.setFillParent(true); dialog.show(getStage()); @@ -945,7 +1160,7 @@ public void changed(ChangeListener.ChangeEvent ce, Actor actor) { minHeightSpinner.getButtonPlus().addListener(main.getHandListener()); minHeightSpinner.getTextField().addListener(main.getIbeamListener()); - okButton.setDisabled(!validateTiledDrawable(drawable, textField.getText(), (ColorData) button.getUserObject())); + okButton.setDisabled(!validateTiledDrawable(tileDialog, textField.getText(), drawable.name, (ColorData) button.getUserObject(), newDrawable)); textField.addListener(new ChangeListener() { @Override @@ -954,7 +1169,8 @@ public void changed(ChangeListener.ChangeEvent event, Button button = tileDialog.findActor("color-selector"); - okButton.setDisabled(!validateTiledDrawable(drawable, textField.getText(), (ColorData) button.getUserObject())); + okButton.setDisabled(!validateTiledDrawable(tileDialog, textField.getText(), drawable.name, (ColorData) button.getUserObject(), newDrawable)); + } }); textField.setTextFieldListener((TextField textField1, char c) -> { @@ -971,17 +1187,44 @@ public void changed(ChangeListener.ChangeEvent event, tileDialog.show(getStage()); } - private boolean validateTiledDrawable(DrawableData drawable, String newName, ColorData colorData) { - boolean returnValue = DrawableData.validate(newName); - - for (DrawableData data : main.getAtlasData().getDrawables()) { - if (data != drawable && data.name.equals(newName)) { + private boolean validateTiledDrawable(Dialog dialog, String newName, String oldName, ColorData colorData, boolean newDrawable) { + boolean returnValue = true; + String requiredLabelName = null; + + if (!DrawableData.validate(newName) || checkIfNameExists(newName)) { + if (newDrawable || !newName.equals(oldName)) { + requiredLabelName = "name-label"; returnValue = false; } + + } else if (colorData == null) { + requiredLabelName = "color-label"; + returnValue = false; } - if (colorData == null) { - returnValue = false; + var normalStyle = getSkin().get(Label.LabelStyle.class); + var requiredStyle = getSkin().get("required", Label.LabelStyle.class); + var actors = new Array(); + actors.addAll(dialog.getChildren()); + + for (int i = 0; i < actors.size; i++) { + var actor = actors.get(i); + + if (actor instanceof Group) { + actors.addAll(((Group) actor).getChildren()); + } + + if (actor instanceof Label) { + Label label = (Label) actor; + + if (label.getStyle().equals(requiredStyle)) { + label.setStyle(normalStyle); + } + + if (requiredLabelName != null && label.getName() != null && label.getName().equals(requiredLabelName)) { + label.setStyle(requiredStyle); + } + } } return returnValue; @@ -1247,6 +1490,18 @@ private boolean checkDuplicateDrawables(String name, int minimum) { return count > minimum; } + private boolean checkDuplicateFontDrawables(String name, int minimum) { + int count = 0; + for (int i = 0; i < main.getAtlasData().getFontDrawables().size; i++) { + DrawableData data = main.getAtlasData().getFontDrawables().get(i); + if (data.name != null && name.equals(data.name)) { + count++; + } + } + + return count > minimum; + } + /** * Removes any duplicate drawables that share the same file name. This * ignores the file extension and also deletes TintedDrawables from the @@ -1346,43 +1601,47 @@ private void showDrawableError() { Label label = new Label("Error while adding new drawables.\nEnsure that image dimensions are\nless than maximums specified in project.\nRolling back changes...", getSkin()); label.setAlignment(Align.center); dialog.text(label); - dialog.button("OK"); + + var textButton = new TextButton("OK", getSkin()); + textButton.addListener(main.getHandListener()); + dialog.button(textButton); dialog.show(getStage()); } private void newDrawableDialog() { - String defaultPath = ""; - - if (main.getProjectData().getLastDrawablePath() != null) { - FileHandle fileHandle = new FileHandle(main.getProjectData().getLastDrawablePath()); - if (fileHandle.parent().exists()) { - defaultPath = main.getProjectData().getLastDrawablePath(); + main.getDialogFactory().showDialogLoading(() -> { + String defaultPath = ""; + + if (main.getProjectData().getLastDrawablePath() != null) { + FileHandle fileHandle = new FileHandle(main.getProjectData().getLastDrawablePath()); + if (fileHandle.parent().exists()) { + defaultPath = main.getProjectData().getLastDrawablePath(); + } } - } - - String[] filterPatterns = null; - if (!Utils.isMac()) { - filterPatterns = new String[] {"*.png", "*.jpg", "*.jpeg", "*.bmp", "*.gif"}; - } - - List files = main.getDesktopWorker().openMultipleDialog("Choose drawable file(s)...", defaultPath, filterPatterns, "Image files"); - if (files != null && files.size() > 0) { - drawablesSelected(files); - } + + String[] filterPatterns = null; + if (!Utils.isMac()) { + filterPatterns = new String[]{"*.png", "*.jpg", "*.jpeg", "*.bmp", "*.gif"}; + } + + List files = main.getDesktopWorker().openMultipleDialog("Choose drawable file(s)...", defaultPath, filterPatterns, "Image files"); + if (files != null && files.size() > 0) { + Gdx.app.postRunnable(() -> { + drawablesSelected(files); + }); + } + }); } private void customDrawableDialog() { Array backup = new Array<>(); - main.getDialogFactory().showCustomDrawableDialog(getSkin(), getStage(), new DialogFactory.CustomDrawableListener() { - @Override - public void run(String name) { - DrawableData drawable = new DrawableData(name); - main.getAtlasData().getDrawables().add(drawable); - - gatherDrawables(); - - main.getDialogFactory().showDialogLoading(() -> { + main.getDialogFactory().showCustomDrawableDialog(getSkin(), getStage(), (String name1) -> { + DrawableData drawable = new DrawableData(name1); + main.getAtlasData().getDrawables().add(drawable); + gatherDrawables(); + main.getDialogFactory().showDialogLoading(() -> { + Gdx.app.postRunnable(() -> { if (!produceAtlas()) { showDrawableError(); Gdx.app.log(getClass().getName(), "Attempting to reload drawables backup..."); @@ -1398,22 +1657,19 @@ public void run(String name) { if (main.getProjectData().areResourcesRelative()) { main.getProjectData().makeResourcesRelative(); } - + main.getProjectData().setChangesSaved(false); } - + sortBySelectedMode(); }); - } + }); }); } private void renameCustomDrawableDialog(DrawableData drawableData) { - main.getDialogFactory().showCustomDrawableDialog(main.getSkin(), main.getStage(), drawableData, new DialogFactory.CustomDrawableListener() { - @Override - public void run(String name) { - renameDrawable(drawableData, name); - } + main.getDialogFactory().showCustomDrawableDialog(main.getSkin(), main.getStage(), drawableData, (String name1) -> { + renameDrawable(drawableData, name1); }); } @@ -1425,9 +1681,9 @@ public void run(String name) { private void drawablesSelected(List files) { Array fileHandles = new Array<>(); - for (File file : files) { + files.forEach((file) -> { fileHandles.add(new FileHandle(file)); - } + }); drawablesSelected(fileHandles); } @@ -1440,7 +1696,9 @@ private void drawablesSelected(Array files) { main.getProjectData().setLastDrawablePath(files.get(0).parent().path() + "/"); for (FileHandle fileHandle : files) { - if (checkDuplicateDrawables(DrawableData.proper(fileHandle.name()), 0)) { + var duplicateDrawable = checkDuplicateDrawables(DrawableData.proper(fileHandle.name()), 0); + var duplicateFontDrawable = checkDuplicateFontDrawables(DrawableData.proper(fileHandle.name()), 0); + if (duplicateDrawable || duplicateFontDrawable) { unhandledFiles.add(fileHandle); } else { filesToProcess.add(fileHandle); @@ -1469,7 +1727,9 @@ protected void result(Object object) { if ((boolean) object) { for (FileHandle fileHandle : unhandledFiles) { removeDuplicateDrawables(DrawableData.proper(fileHandle.name()), false); - filesToProcess.add(fileHandle); + if (!checkDuplicateFontDrawables(DrawableData.proper(fileHandle.name()), 0)) { + filesToProcess.add(fileHandle); + } } } finalizeDrawables(backup, filesToProcess); @@ -1482,8 +1742,23 @@ protected void result(Object object) { dialog.getContentTable().padLeft(10.0f).padRight(10.0f).padTop(5.0f); dialog.getButtonTable().padBottom(15.0f); - dialog.text("Adding this drawable will overwrite one or more drawables\n" - + "Delete duplicates?"); + var containsFontDrawable = false; + for (FileHandle fileHandle : unhandledFiles) { + if (checkDuplicateFontDrawables(DrawableData.proper(fileHandle.name()), 0)) { + containsFontDrawable = true; + break; + } + } + + if (containsFontDrawable) { + dialog.text("This operation will overwrite one or more drawables\n" + + "Delete duplicates?\n\n" + + "Note: drawables that overwrite drawables used by fonts can not be added."); + } else { + dialog.text("This operation will overwrite one or more drawables\n" + + "Delete duplicates?"); + } + dialog.button("OK", true); dialog.button("Cancel", false); dialog.getButtonTable().getCells().first().getActor().addListener(main.getHandListener()); @@ -1510,26 +1785,28 @@ private void finalizeDrawables(Array backup, Array fil gatherDrawables(); main.getDialogFactory().showDialogLoading(() -> { - if (!produceAtlas()) { - showDrawableError(); - Gdx.app.log(getClass().getName(), "Attempting to reload drawables backup..."); - main.getAtlasData().getDrawables().clear(); - main.getAtlasData().getDrawables().addAll(backup); - gatherDrawables(); - if (produceAtlas()) { - Gdx.app.log(getClass().getName(), "Successfully rolled back changes to drawables"); + Gdx.app.postRunnable(() -> { + if (!produceAtlas()) { + showDrawableError(); + Gdx.app.log(getClass().getName(), "Attempting to reload drawables backup..."); + main.getAtlasData().getDrawables().clear(); + main.getAtlasData().getDrawables().addAll(backup); + gatherDrawables(); + if (produceAtlas()) { + Gdx.app.log(getClass().getName(), "Successfully rolled back changes to drawables"); + } else { + Gdx.app.error(getClass().getName(), "Critical failure, could not roll back changes to drawables"); + } } else { - Gdx.app.error(getClass().getName(), "Critical failure, could not roll back changes to drawables"); - } - } else { - if (main.getProjectData().areResourcesRelative()) { - main.getProjectData().makeResourcesRelative(); + if (main.getProjectData().areResourcesRelative()) { + main.getProjectData().makeResourcesRelative(); + } + + main.getProjectData().setChangesSaved(false); } - - main.getProjectData().setChangesSaved(false); - } - sortBySelectedMode(); + sortBySelectedMode(); + }); }); } @@ -1639,12 +1916,16 @@ public boolean keyDown(InputEvent event, int keycode2) { }); } + private boolean checkIfNameExists(String name) { + return checkIfDrawableNameExists(name) || checkIfFontDrawableNameExists(name); + } + /** * Returns true if any existing drawable has the indicated name. * @param name * @return */ - private boolean checkIfNameExists(String name) { + private boolean checkIfDrawableNameExists(String name) { boolean returnValue = false; for (DrawableData drawable : main.getAtlasData().getDrawables()) { @@ -1657,6 +1938,24 @@ private boolean checkIfNameExists(String name) { return returnValue; } + /** + * Returns true if any existing drawable has the indicated name. + * @param name + * @return + */ + private boolean checkIfFontDrawableNameExists(String name) { + boolean returnValue = false; + + for (DrawableData drawable : main.getAtlasData().getFontDrawables()) { + if (drawable.name.equals(name)) { + returnValue = true; + break; + } + } + + return returnValue; + } + @Override public boolean remove() { scrollPosition = scrollPane.getScrollY(); @@ -1665,7 +1964,7 @@ public boolean remove() { try { if (!main.getAtlasData().atlasCurrent) { - main.getAtlasData().writeAtlas(); + main.getAtlasData().writeAtlas(Gdx.files.internal("atlas-internal-settings.json")); main.getAtlasData().atlasCurrent = true; } } catch (Exception e) { @@ -1820,31 +2119,35 @@ public FilterInputListener(DialogDrawables dialog) { @Override public boolean keyTyped(InputEvent event, char character) { - Label label = dialog.findActor("filter-label"); - if (!label.isVisible()) { - name = ""; - } - - var filterOptions = dialog.filterOptions; - filterOptions.regularExpression = false; - filterOptions.applied = true; - if (character == 8) { - if (name.length() > 0) { - name = name.substring(0, name.length() - 1); + //not enter + if (character != 10) { + Label label = dialog.findActor("filter-label"); + if (!label.isVisible()) { + name = ""; } - } else { - name += character; + + var filterOptions = dialog.filterOptions; + filterOptions.regularExpression = false; + filterOptions.applied = true; + //backspace + if (character == 8) { + if (name.length() > 0) { + name = name.substring(0, name.length() - 1); + } + } else { + name += character; + } + filterOptions.name = name; + + Button button = dialog.findActor("filter"); + button.setChecked(true); + + label.setText(name); + label.clearActions(); + label.addAction(Actions.sequence(Actions.visible(true), Actions.fadeIn(.25f), Actions.delay(2.0f), Actions.fadeOut(.25f), Actions.visible(false))); + + dialog.sortBySelectedMode(); } - filterOptions.name = name; - - Button button = dialog.findActor("filter"); - button.setChecked(true); - - label.setText(name); - label.clearActions(); - label.addAction(Actions.sequence(Actions.visible(true), Actions.fadeIn(.25f), Actions.delay(2.0f), Actions.fadeOut(.25f), Actions.visible(false))); - - dialog.sortBySelectedMode(); return super.keyTyped(event, character); } diff --git a/core/src/com/ray3k/skincomposer/dialog/DialogExport.java b/core/src/com/ray3k/skincomposer/dialog/DialogExport.java index 62387506..9ec1fd89 100644 --- a/core/src/com/ray3k/skincomposer/dialog/DialogExport.java +++ b/core/src/com/ray3k/skincomposer/dialog/DialogExport.java @@ -42,6 +42,8 @@ import com.ray3k.skincomposer.data.FontData; import com.ray3k.skincomposer.data.FreeTypeFontData; import com.ray3k.skincomposer.data.JsonData.ExportFormat; +import com.ray3k.skincomposer.utils.Utils; + import java.nio.file.Paths; /** @@ -130,6 +132,18 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { } }); + getContentTable().row(); + var hexCheckBox = new CheckBox("Export colors as hexadecimal", main.getSkin()); + hexCheckBox.setChecked(main.getProjectData().isExportingHex()); + getContentTable().add(hexCheckBox); + hexCheckBox.addListener(main.getHandListener()); + hexCheckBox.addListener(new ChangeListener() { + @Override + public void changed(ChangeListener.ChangeEvent event, Actor actor) { + main.getProjectData().setExportingHex(hexCheckBox.isChecked()); + } + }); + getContentTable().row(); table = new Table(); getContentTable().add(table); @@ -143,6 +157,7 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { selectBox.setSelected(main.getProjectData().getExportFormat()); table.add(selectBox); selectBox.addListener(main.getHandListener()); + selectBox.getList().addListener(main.getHandListener()); selectBox.addListener(new ChangeListener() { @Override public void changed(ChangeListener.ChangeEvent event, Actor actor) { @@ -168,7 +183,10 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { } private void showFileBrowser() { - String[] filterPatterns = {"*.json"}; + String[] filterPatterns = null; + if (!Utils.isMac()) { + filterPatterns = new String[] {"*.json"}; + } TextField textField = findActor("path"); var file = main.getDesktopWorker().saveDialog("Export skin...", textField.getText(), filterPatterns, "Json files"); @@ -203,38 +221,40 @@ protected void result(Object object) { private void writeFile(FileHandle fileHandle) { main.getDialogFactory().showDialogLoading(() -> { - Array warnings = new Array<>(); + Gdx.app.postRunnable(() -> { + Array warnings = new Array<>(); - Array newWarnings = main.getProjectData().getJsonData().writeFile(fileHandle); - warnings.addAll(newWarnings); + Array newWarnings = main.getProjectData().getJsonData().writeFile(fileHandle); + warnings.addAll(newWarnings); - if (main.getProjectData().isExportingAtlas()) { - try { - newWarnings = main.getProjectData().getAtlasData().writeAtlas(fileHandle.parent().child(fileHandle.nameWithoutExtension() + ".atlas")); - warnings.addAll(newWarnings); - } catch (Exception ex) { - Gdx.app.error(getClass().getName(), "Error while writing texture atlas", ex); - main.getDialogFactory().showDialogError("Atlas Error...", "Error while writing texture atlas.\n\nOpen log?"); + if (main.getProjectData().isExportingAtlas()) { + try { + newWarnings = main.getProjectData().getAtlasData().writeAtlas(fileHandle.parent().child(fileHandle.nameWithoutExtension() + ".atlas"), Main.appFolder.child("texturepacker/atlas-export-settings.json")); + warnings.addAll(newWarnings); + } catch (Exception ex) { + Gdx.app.error(getClass().getName(), "Error while writing texture atlas", ex); + main.getDialogFactory().showDialogError("Atlas Error...", "Error while writing texture atlas.\n\nOpen log?"); + } } - } - if (main.getProjectData().isExportingFonts()) { - for (FontData font : main.getProjectData().getJsonData().getFonts()) { - if (!font.file.parent().equals(fileHandle.parent())) { - font.file.copyTo(fileHandle.parent()); + if (main.getProjectData().isExportingFonts()) { + for (FontData font : main.getProjectData().getJsonData().getFonts()) { + if (!font.file.parent().equals(fileHandle.parent())) { + font.file.copyTo(fileHandle.parent()); + } } - } - for (FreeTypeFontData font : main.getProjectData().getJsonData().getFreeTypeFonts()) { - if (font.useCustomSerializer && !font.file.parent().equals(fileHandle.parent())) { - font.file.copyTo(fileHandle.parent()); + for (FreeTypeFontData font : main.getProjectData().getJsonData().getFreeTypeFonts()) { + if (font.useCustomSerializer && !font.file.parent().equals(fileHandle.parent())) { + font.file.copyTo(fileHandle.parent()); + } } } - } - if (warnings.size > 0) { - main.getDialogFactory().showWarningDialog(warnings); - } + if (warnings.size > 0 && main.getProjectData().isShowingExportWarnings()) { + main.getDialogFactory().showWarningDialog(warnings); + } + }); }); } diff --git a/core/src/com/ray3k/skincomposer/dialog/DialogFactory.java b/core/src/com/ray3k/skincomposer/dialog/DialogFactory.java index 8cb87a96..0c89425d 100644 --- a/core/src/com/ray3k/skincomposer/dialog/DialogFactory.java +++ b/core/src/com/ray3k/skincomposer/dialog/DialogFactory.java @@ -557,7 +557,9 @@ protected void result(Object object) { if ((int) object == 0) { main.getMainListener().saveFile(() -> { if (main.getProjectData().areChangesSaved()) { - Gdx.app.exit(); + Gdx.app.postRunnable(() -> { + Gdx.app.exit(); + }); } }); } else if ((int) object == 1) { @@ -768,12 +770,16 @@ protected void result(Object object) { if (selectBox.getSelected() != null) { FileHandle file = selectBox.getSelected().getFileHandle(); if (file.exists()) { - main.getProjectData().load(file); - Array drawableErrors = main.getProjectData().verifyDrawablePaths(); - Array fontErrors = main.getProjectData().verifyFontPaths(); - if (drawableErrors.size > 0 || fontErrors.size > 0) { - main.getDialogFactory().showDialogPathErrors(drawableErrors, fontErrors); - } + showDialogLoading(() -> { + Gdx.app.postRunnable(() -> { + main.getProjectData().load(file); + Array drawableErrors = main.getProjectData().verifyDrawablePaths(); + Array fontErrors = main.getProjectData().verifyFontPaths(); + if (drawableErrors.size > 0 || fontErrors.size > 0) { + main.getDialogFactory().showDialogPathErrors(drawableErrors, fontErrors); + } + }); + }); } } } diff --git a/core/src/com/ray3k/skincomposer/dialog/DialogFonts.java b/core/src/com/ray3k/skincomposer/dialog/DialogFonts.java index 16792568..1ed98868 100644 --- a/core/src/com/ray3k/skincomposer/dialog/DialogFonts.java +++ b/core/src/com/ray3k/skincomposer/dialog/DialogFonts.java @@ -49,6 +49,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.TextField; import com.badlogic.gdx.scenes.scene2d.ui.TextTooltip; import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; +import com.badlogic.gdx.scenes.scene2d.utils.Drawable; import com.badlogic.gdx.utils.Align; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.JsonReader; @@ -77,6 +78,7 @@ public class DialogFonts extends Dialog { private Array fonts; private Array freeTypeFonts; private Array drawables; + private Array fontDrawables; private Table fontsTable; private SelectBox selectBox; private ObjectMap fontMap; @@ -94,8 +96,8 @@ public void initialize(Main main, EventListener listener) { maxTextureWidth = 1024; maxTextureHeight = 1024; - //extract max texture dimensions from defaults.json - FileHandle defaultsFile = Main.appFolder.child("texturepacker/defaults.json"); + //extract max texture dimensions from atlas-export-settings.json + FileHandle defaultsFile = Main.appFolder.child("texturepacker/atlas-export-settings.json"); if (defaultsFile.exists()) { JsonReader reader = new JsonReader(); JsonValue val = reader.parse(defaultsFile); @@ -114,6 +116,7 @@ public void initialize(Main main, EventListener listener) { fonts = main.getJsonData().getFonts(); freeTypeFonts = main.getJsonData().getFreeTypeFonts(); drawables = main.getAtlasData().getDrawables(); + fontDrawables = main.getAtlasData().getFontDrawables(); fontMap = new ObjectMap<>(); produceAtlas(); @@ -271,19 +274,39 @@ private boolean addFont(String name, FileHandle file) { BitmapFontData deleteFontData = new BitmapFontData(deleteFont.file, false); for (String path : deleteFontData.imagePaths) { FileHandle imagefile = new FileHandle(path); - drawables.removeValue(new DrawableData(imagefile), false); + var drawable = main.getAtlasData().getFontDrawable(imagefile.nameWithoutExtension()); + if (drawable != null) { + fontDrawables.removeValue(drawable, false); + } } fonts.removeValue(font, false); } - BitmapFontData bitmapFontData = new BitmapFontData(file, false); + var bitmapFontData = new BitmapFontData(file, false); for (String path : bitmapFontData.imagePaths) { - DrawableData drawable = new DrawableData(new FileHandle(path)); - drawable.visible = false; - if (!drawables.contains(drawable, false)) { + //remove any existing drawables that share the name + FileHandle imagefile = new FileHandle(path); + var duplicateDrawable = main.getAtlasData().getDrawable(imagefile.nameWithoutExtension()); + if (duplicateDrawable != null) { + drawables.removeValue(duplicateDrawable, false); + for (Array datas : main.getJsonData().getClassStyleMap().values()) { + for (StyleData data : datas) { + for (StyleProperty styleProperty : data.properties.values()) { + if (styleProperty != null && styleProperty.type.equals(Drawable.class) && styleProperty.value != null && styleProperty.value.equals(duplicateDrawable.toString())) { + styleProperty.value = null; + } + } + } + } + main.getRootTable().refreshStyleProperties(true); + main.getRootTable().refreshPreview(); + } + + var drawable = new DrawableData(new FileHandle(path)); + if (!fontDrawables.contains(drawable, false)) { main.getAtlasData().atlasCurrent = false; - drawables.add(drawable); + fontDrawables.add(drawable); } } produceAtlas(); @@ -300,7 +323,7 @@ private boolean addFont(String name, FileHandle file) { sortBySelectedMode(); refreshTable(); - } catch (Exception e) { + } catch (FontData.NameFormatException e) { Gdx.app.error(getClass().getName(), "Error creating font from file", e); main.getDialogFactory().showDialogError("Font Error...", "Error creating font from file. Check file paths.\n\nOpen log?"); } @@ -315,11 +338,11 @@ public void refreshTable() { fontsTable.defaults().growX().pad(5.0f); if (fonts.size == 0 && freeTypeFonts.size == 0) { - fontsTable.add(new Label("No fonts have been set!", getSkin())); + fontsTable.add(new Label("No fonts have been set!", getSkin(), "black")); } else { if (fonts.size > 0) { - Label label = new Label("Bitmap Fonts", getSkin(), "required"); + Label label = new Label("Bitmap Fonts", getSkin(), "black"); label.setAlignment(Align.center); fontsTable.add(label); fontsTable.row(); @@ -387,7 +410,7 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { BitmapFontData bitmapFontData = new BitmapFontData(deleteFont.file, false); for (String path : bitmapFontData.imagePaths) { FileHandle imagefile = new FileHandle(path); - drawables.removeValue(new DrawableData(imagefile), false); + fontDrawables.removeValue(new DrawableData(imagefile), false); } for (Array datas : main.getJsonData().getClassStyleMap().values()) { @@ -440,7 +463,7 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { } if (freeTypeFonts.size > 0) { - Label label = new Label("FreeType Fonts", getSkin(), "required"); + Label label = new Label("FreeType Fonts", getSkin(), "black"); label.setAlignment(Align.center); fontsTable.add(label).spaceTop(20.0f); fontsTable.row(); @@ -626,14 +649,11 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { okButton.setDisabled(disable); } }); - textField.setTextFieldListener(new TextField.TextFieldListener() { - @Override - public void keyTyped(TextField textField, char c) { - if (c == '\n') { - if (!okButton.isDisabled()) { - renameFont(font, textField.getText()); - dialog.hide(); - } + textField.setTextFieldListener((TextField textField1, char c) -> { + if (c == '\n') { + if (!okButton.isDisabled()) { + renameFont(font, textField1.getText()); + dialog.hide(); } } }); @@ -858,7 +878,7 @@ private boolean produceAtlas() { } if (!main.getAtlasData().atlasCurrent) { - main.getAtlasData().writeAtlas(); + main.getAtlasData().writeAtlas(Gdx.files.internal("atlas-internal-settings.json")); main.getAtlasData().atlasCurrent = true; } atlas = main.getAtlasData().getAtlas(); @@ -887,25 +907,77 @@ private boolean produceAtlas() { } private void newFontDialog() { - String defaultPath = ""; - - if (main.getProjectData().getLastFontPath() != null) { - FileHandle fileHandle = new FileHandle(main.getProjectData().getLastFontPath()); - if (fileHandle.exists()) { - defaultPath = main.getProjectData().getLastFontPath(); + main.getDialogFactory().showDialogLoading(() -> { + String defaultPath = ""; + + if (main.getProjectData().getLastFontPath() != null) { + FileHandle fileHandle = new FileHandle(main.getProjectData().getLastFontPath()); + if (fileHandle.exists()) { + defaultPath = main.getProjectData().getLastFontPath(); + } } - } + + String[] filterPatterns = null; + if (!Utils.isMac()) { + filterPatterns = new String[]{"*.fnt"}; + } + + List files = main.getDesktopWorker().openMultipleDialog("Choose font file(s)...", defaultPath, filterPatterns, "Font files (*.fnt)"); + if (files != null && files.size() > 0) { + Gdx.app.postRunnable(() -> { + FileHandle fileHandle = new FileHandle(files.get(0).getParentFile()); + main.getProjectData().setLastFontPath(fileHandle.path() + "/"); + checkExistingDrawablesDialog(files, () -> { + fontNameDialog(files, 0); + }); + }); + } + }); + } + + private void checkExistingDrawablesDialog(List files, Runnable runnable) { + boolean execute = true; - String[] filterPatterns = null; - if (!Utils.isMac()) { - filterPatterns = new String[] {"*.fnt"}; + fontLoop : for (var fontFile : files) { + var bitmapFontData = new BitmapFontData(new FileHandle(fontFile), false); + for (var imagePath : bitmapFontData.imagePaths) { + var imageFile = new FileHandle(imagePath); + if (main.getAtlasData().getDrawable(imageFile.nameWithoutExtension()) != null) { + execute = false; + break fontLoop; + } + } } - List files = main.getDesktopWorker().openMultipleDialog("Choose font file(s)...", defaultPath, filterPatterns, "Font files (*.fnt)"); - if (files != null && files.size() > 0) { - FileHandle fileHandle = new FileHandle(files.get(0).getParentFile()); - main.getProjectData().setLastFontPath(fileHandle.path() + "/"); - fontNameDialog(files, 0); + if (execute) { + runnable.run(); + } else { + var dialog = new Dialog("Override Drawables?", getSkin()) { + @Override + protected void result(Object object) { + if ((Boolean) object) { + runnable.run(); + } + } + }; + dialog.getTitleTable().padLeft(5); + dialog.getContentTable().pad(5).padBottom(0); + dialog.getButtonTable().pad(5); + + dialog.text("This font will override existing drawables.\n" + + "Proceed?"); + + var textButton = new TextButton("OK", getSkin()); + textButton.addListener(main.getHandListener()); + dialog.button(textButton, true); + + textButton = new TextButton("Cancel", getSkin()); + textButton.addListener(main.getHandListener()); + dialog.button(textButton, false); + + dialog.key(Keys.ENTER, true).key(Keys.ESCAPE, false); + + dialog.show(getStage()); } } @@ -953,9 +1025,9 @@ private void newImageFontDialog() { private void fontNameDialog(List files, int index) { Array handles = new Array<>(); - for (File file : files) { + files.forEach((file) -> { handles.add(new FileHandle(file)); - } + }); fontNameDialog(handles, index); } diff --git a/core/src/com/ray3k/skincomposer/dialog/DialogFreeTypeFont.java b/core/src/com/ray3k/skincomposer/dialog/DialogFreeTypeFont.java index 02e89fa4..404d984a 100644 --- a/core/src/com/ray3k/skincomposer/dialog/DialogFreeTypeFont.java +++ b/core/src/com/ray3k/skincomposer/dialog/DialogFreeTypeFont.java @@ -31,6 +31,7 @@ import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.scenes.scene2d.Actor; +import com.badlogic.gdx.scenes.scene2d.Group; import com.badlogic.gdx.scenes.scene2d.InputEvent; import com.badlogic.gdx.scenes.scene2d.Touchable; import com.badlogic.gdx.scenes.scene2d.actions.Actions; @@ -257,7 +258,7 @@ public void addListener(DialogFreeTypeFontListener listener) { private void populate() { root.pad(15.0f); - Label label = new Label(mode == Mode.NEW ? "Create a new FreeType Font placeholder." : "Edit FreeType Font placeholder.", skin, "required"); + Label label = new Label(mode == Mode.NEW ? "Create a new FreeType Font placeholder." : "Edit FreeType Font placeholder.", skin, "black"); root.add(label); root.row(); @@ -266,6 +267,7 @@ private void populate() { table.defaults().space(5.0f); label = new Label("Font Name:", skin); + label.setName("name-label"); table.add(label); TextField textField = new TextField(data.name, skin); @@ -313,6 +315,7 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { selectBox.addListener(toolTip); selectBox.addListener(main.getHandListener()); + selectBox.getList().addListener(main.getHandListener()); selectBox.addListener(new ChangeListener() { @Override public void changed(ChangeListener.ChangeEvent event, Actor actor) { @@ -455,6 +458,7 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { table.defaults().space(5.0f); label = new Label("TTF Path:", skin); + label.setName("source-label"); table.add(label).right(); textField = new TextField(data.file == null ? "" : data.file.path(), skin); @@ -491,8 +495,10 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { File file = main.getDesktopWorker().openDialog("Select TTF file...", defaultPath, filterPatterns, "True Type Font files"); if (file != null) { - FileHandle fileHandle = new FileHandle(file); - loadTTF(fileHandle); + Gdx.app.postRunnable(() -> { + FileHandle fileHandle = new FileHandle(file); + loadTTF(fileHandle); + }); } }; @@ -531,12 +537,14 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { selectBox = new SelectBox(skin); selectBox.setName("character-select-box"); - selectBox.setItems("default", "0-9", "a-zA-Z", "a-zA-Z0-9", "custom"); + selectBox.setItems("default", "0-9", "a-zA-Z", "a-zA-Z0-9", "custom", "Load from file (UTF-8)..."); table.add(selectBox); if (!data.characters.equals("")) { selectBox.setSelected("custom"); } + selectBox.addListener(main.getHandListener()); + selectBox.getList().addListener(main.getHandListener()); selectBox.addListener(new ChangeListener() { @Override public void changed(ChangeListener.ChangeEvent event, Actor actor) { @@ -546,19 +554,32 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { switch (selectBox.getSelected()) { case "default": textField.setText(""); + textField.setMessageText(""); + data.characters = ""; break; case "0-9": textField.setText("0123456789"); + textField.setMessageText(""); + data.characters = textField.getText(); break; case "a-zA-Z": textField.setText("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + textField.setMessageText(""); + data.characters = textField.getText(); break; case "a-zA-Z0-9": textField.setText("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"); + textField.setMessageText(""); + data.characters = textField.getText(); + break; + case "Load from file (UTF-8)...": + textField.setText(""); + textField.setMessageText("Characters loaded from text file..."); + data.characters = ""; + showCharacterDialog(); break; } - data.characters = textField.getText(); data.characters = !data.characters.equals("") && !data.characters.contains("\u0000") ? "\u0000" + data.characters : data.characters; updateDisabledFields(); } @@ -626,6 +647,7 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { selectBox.addListener(toolTip); selectBox.addListener(main.getHandListener()); + selectBox.getList().addListener(main.getHandListener()); selectBox.addListener(new ChangeListener() { @Override public void changed(ChangeListener.ChangeEvent event, Actor actor) { @@ -637,6 +659,7 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { }); label = new Label("Color:", skin); + label.setName("color-label"); bottom.add(label).right(); textButton = new TextButton(data.color, skin); @@ -743,6 +766,7 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { }); label = new Label("Border Color:", skin); + label.setName("border-color-label"); bottom.add(label).right(); textButton = new TextButton(data.borderColor, skin); @@ -871,6 +895,7 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { bottom.row(); label = new Label("Shadow Color:", skin); + label.setName("shadow-color-label"); bottom.add(label).right(); textButton = new TextButton(data.shadowColor, skin); @@ -1057,6 +1082,7 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { selectBox.addListener(toolTip); selectBox.addListener(main.getHandListener()); + selectBox.getList().addListener(main.getHandListener()); selectBox.addListener(new ChangeListener() { @Override public void changed(ChangeListener.ChangeEvent event, Actor actor) { @@ -1080,6 +1106,7 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { selectBox.addListener(toolTip); selectBox.addListener(main.getHandListener()); + selectBox.getList().addListener(main.getHandListener()); selectBox.addListener(new ChangeListener() { @Override public void changed(ChangeListener.ChangeEvent event, Actor actor) { @@ -1165,10 +1192,22 @@ private void updateDisabledFields() { boolean notValid = false; if (checkBox.isChecked()) { - if (data.color == null) notValid = true; - else if (data.file == null || !data.file.exists()) notValid = true; - else if (!MathUtils.isZero(data.borderWidth) && data.borderColor == null) notValid = true; - else if ((data.shadowOffsetX != 0 || data.shadowOffsetY != 0) && data.shadowColor == null) notValid = true; + if (data.color == null) { + notValid = true; + updateLabelHighlight("color-label"); + } + else if (data.file == null || !data.file.exists()) { + notValid = true; + updateLabelHighlight("source-label"); + } + else if (!MathUtils.isZero(data.borderWidth) && data.borderColor == null) { + notValid = true; + updateLabelHighlight("border-color-label"); + } + else if ((data.shadowOffsetX != 0 || data.shadowOffsetY != 0) && data.shadowColor == null) { + notValid = true; + updateLabelHighlight("shadow-color-label"); + } } if (notValid) { @@ -1208,11 +1247,15 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { } } - if (!StyleData.validate(((TextField)findActor("fontName")).getText())) notValid = true; + if (!StyleData.validate(((TextField)findActor("fontName")).getText())) { + notValid = true; + updateLabelHighlight("name-label"); + } for (FontData font : main.getJsonData().getFonts()) { if (font.getName().equals(data.name)) { notValid = true; + updateLabelHighlight("name-label"); break; } } @@ -1220,10 +1263,15 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { for (FreeTypeFontData font : main.getJsonData().getFreeTypeFonts()) { if (font.name.equals(data.name) && (mode == Mode.NEW || !font.name.equals(originalData.name))) { notValid = true; + updateLabelHighlight("name-label"); break; } } + if (!notValid) { + updateLabelHighlight(null); + } + TextButton textButton = findActor("okButton"); textButton.setDisabled(notValid); } @@ -1285,13 +1333,15 @@ private void saveSettings() { File file = main.getDesktopWorker().saveDialog("Save Bitmap Font settings...", defaultPath, filterPatterns, "Font Settings files"); if (file != null) { - var fileHandle = new FileHandle(file); - - if (!fileHandle.extension().toLowerCase(Locale.ROOT).equals("scmp-font")) { - fileHandle = fileHandle.sibling(fileHandle.name() + ".scmp-font"); - } - - saveSettings(fileHandle); + Gdx.app.postRunnable(() -> { + var fileHandle = new FileHandle(file); + + if (!fileHandle.extension().toLowerCase(Locale.ROOT).equals("scmp-font")) { + fileHandle = fileHandle.sibling(fileHandle.name() + ".scmp-font"); + } + + saveSettings(fileHandle); + }); } }; @@ -1300,7 +1350,7 @@ private void saveSettings() { private void saveSettings(FileHandle fileHandle) { var fontSettings = new FontSettings(); - fontSettings.characters = ((TextField) findActor("characters")).getText(); + fontSettings.characters = data.characters; fontSettings.size = ((Spinner) findActor("size")).getValueAsInt(); fontSettings.mono = ((Button) findActor("mono")).isChecked(); fontSettings.hinting = ((SelectBox) findActor("hinting")).getSelected(); @@ -1338,7 +1388,7 @@ private void saveSettings(FileHandle fileHandle) { fontSettings.minFilter = ((SelectBox) findActor("minFilter")).getSelected(); fontSettings.magFilter = ((SelectBox) findActor("magFilter")).getSelected(); - fileHandle.writeString(json.prettyPrint(fontSettings), false); + fileHandle.writeString(json.prettyPrint(fontSettings), false, "utf-8"); } private static class FontSettings { @@ -1381,7 +1431,9 @@ private void loadSettings() { File file = main.getDesktopWorker().openDialog("Select Bitmap Font settings...", defaultPath, filterPatterns, "Font Settings files"); if (file != null) { - loadSettings(new FileHandle(file)); + Gdx.app.postRunnable(() -> { + loadSettings(new FileHandle(file)); + }); } }; @@ -1389,7 +1441,7 @@ private void loadSettings() { } private void loadSettings(FileHandle fileHandle) { - var fontSettings = json.fromJson(FontSettings.class, fileHandle); + var fontSettings = json.fromJson(FontSettings.class, fileHandle.readString("utf-8")); ((TextField) findActor("characters")).setText(fontSettings.characters); data.characters = fontSettings.characters; @@ -1493,4 +1545,54 @@ private void loadSettings(FileHandle fileHandle) { updateColors(); updateDisabledFields(); } -} + + private void updateLabelHighlight(String requiredLabelName) { + var normalStyle = skin.get(Label.LabelStyle.class); + var requiredStyle = skin.get("required", Label.LabelStyle.class); + var actors = new Array(); + actors.addAll(getChildren()); + + for (int i = 0; i < actors.size; i++) { + var actor = actors.get(i); + + if (actor instanceof Group) { + actors.addAll(((Group) actor).getChildren()); + } + + if (actor instanceof Label) { + Label label = (Label) actor; + + if (label.getStyle().equals(requiredStyle)) { + label.setStyle(normalStyle); + } + + if (requiredLabelName != null && label.getName() != null && label.getName().equals(requiredLabelName)) { + label.setStyle(requiredStyle); + } + } + } + } + + private void showCharacterDialog() { + Runnable runnable = () -> { + String defaultPath = main.getProjectData().getLastFontPath(); + + File file = main.getDesktopWorker().openDialog("Select character text file...", defaultPath, null, "All files"); + if (file != null) { + var fileHandle = new FileHandle(file); + String characters = fileHandle.readString("utf-8"); + Gdx.app.postRunnable(() -> { + data.characters = Utils.removeDuplicateCharacters(characters); + updateDisabledFields(); + }); + } else { + Gdx.app.postRunnable(() -> { + SelectBox selectBox = findActor("character-select-box"); + selectBox.setSelected("default"); + }); + } + }; + + main.getDialogFactory().showDialogLoading(runnable); + } +} \ No newline at end of file diff --git a/core/src/com/ray3k/skincomposer/dialog/DialogImageFont.java b/core/src/com/ray3k/skincomposer/dialog/DialogImageFont.java index efc28437..a000ee75 100644 --- a/core/src/com/ray3k/skincomposer/dialog/DialogImageFont.java +++ b/core/src/com/ray3k/skincomposer/dialog/DialogImageFont.java @@ -34,6 +34,7 @@ import com.badlogic.gdx.math.Interpolation; import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.scenes.scene2d.Actor; +import com.badlogic.gdx.scenes.scene2d.Group; import com.badlogic.gdx.scenes.scene2d.InputEvent; import com.badlogic.gdx.scenes.scene2d.InputListener; import com.badlogic.gdx.scenes.scene2d.Touchable; @@ -66,7 +67,7 @@ import com.ray3k.skincomposer.data.StyleProperty; import com.ray3k.skincomposer.utils.Utils; import java.io.File; -import java.io.FilenameFilter; +import java.io.IOException; import java.util.stream.Stream; /** @@ -83,7 +84,7 @@ public class DialogImageFont extends Dialog { private static final String ALL = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789~`!@#$%^&*()_-+=[{]}\\|;:'\",<.>/?•©¿¡áéíóúüñÑÁÉÍÓÚÜ"; private static final String SAMPLE_TEXT = "abcdefghijklmnopqrstuvwxyz\nABCDEFGHIJKLMNOPQRSTUVWXYZ\n0123456789\n~`!@#$%^&*()_-+=[{]}\\|;:'\",<.>/?•©\n¿¡áéíóúüñÑÁÉÍÓÚÜ\n\nThe most merciful thing in the world, I think, is the inability of the human mind to correlate all its contents.\n\n\"That is not dead which can eternal lie, And with strange aeons even death may die.\"\n\nIn his house at R'lyeh dead Cthulhu waits dreaming.\n\nThe Thing cannot be described - there is no language for such abysms of shrieking and immemorial lunacy, such eldritch contradictions of all matter, force, and cosmic order. A mountain walked or stumbled.\n\nWe live on a placid island of ignorance in the midst of black seas of infinity, and it was not meant that we should voyage far."; private static final String KERNING_DEFAULTS = "A' AC AG AO AQ AT AU AV AW AY BA BE BL BP BR BU BV BW BY CA CO CR DA DD DE DI DL DM DN DO DP DR DU DV DW DY EC EO FA FC FG FO F. F, GE GO GR GU HO IC IG IO JA JO KO L' LC LT LV LW LY LG LO LU M MG MO NC NG NO OA OB OD OE OF OH OI OK OL OM ON OP OR OT OU OV OW OX OY PA PE PL PO PP PU PY P. P, P; P: QU RC RG RY RT RU RV RW RY SI SM ST SU TA TC TO UA UC UG UO US VA VC VG VO VS WA WC WG WO YA YC YO YS ZO Ac Ad Ae Ag Ao Ap Aq At Au Av Aw Ay Bb Bi Bk Bl Br Bu By B. B, Ca Cr C. C, Da D. D, Eu Ev Fa Fe Fi Fo Fr Ft Fu Fy F. F, F; F: Gu He Ho Hu Hy Ic Id Iq Io It Ja Je Jo Ju J. J, Ke Ko Ku Lu Ly Ma Mc Md Me Mo Nu Na Ne Ni No Nu N. N, Oa Ob Oh Ok Ol O. O, Pa Pe Po Rd Re Ro Rt Ru Si Sp Su S. S, Ta Tc Te Ti To Tr Ts Tu Tw Ty T. T, T; T: Ua Ug Um Un Up Us U. U, Va Ve Vi Vo Vr Vu V. V, V; V: Wd Wi Wm Wr Wt Wu Wy W. W, W; W: Xa Xe Xo Xu Xy Yd Ye Yi Yp Yu Yv Y. Y, Y; Y: ac ad ae ag ap af at au av aw ay ap bl br bu by b. b, ca ch ck da dc de dg do dt du dv dw dy d. d, ea ei el em en ep er et eu ev ew ey e. e, fa fe ff fi fl fo f. f, ga ge gh gl go gg g. g, hc hd he hg ho hp ht hu hv hw hy ic id ie ig io ip it iu iv ja je jo ju j. j, ka kc kd ke kg ko la lc ld le lf lg lo lp lq lu lv lw ly ma mc md me mg mn mo mp mt mu mv my nc nd ne ng no np nt nu nv nw ny ob of oh oj ok ol om on op or ou ov ow ox oy o. o, pa ph pi pl pp pu p. p, qu t. ra rd re rg rk rl rm rn ro rq rr rt rv ry r. r, sh st su s. s, td ta te to t. t, ua uc ud ue ug uo up uq ut uv uw uy va vb vc vd ve vg vo vv vy v. v, wa wx wd we wg wh wo w. w, xa xe xo y. y, ya yc yd ye yo 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99"; - private static final CharArray BASELINE_EXCLUSION = new CharArray(new char[] {'C', 'G', 'J', 'O', 'Q', 'U', '0', '3', '4', '5', '6', '7', '8', '9', 'c', 'o', 'g', 'j', 'p', 'q', 'y'}); + private static final CharArray BASELINE_EXCLUSION = new CharArray(new char[] {'C', 'G', 'J', 'O', 'Q', 'U', '0', '3', '4', '5', '6', '7', '8', '9', 'c', 'o', 'g', 'j', 'p', 'q', 'y', '_', '-', '=', '|'}); private Array bitmapCharacters; private BitmapFont previewFont; private TextFieldStyle previewStyle; @@ -110,7 +111,9 @@ public DialogImageFont(Main main, ImageFontListener imageFontListener) { filesDroppedListener = (Array files) -> { if (files.size > 0 && files.first().extension().equalsIgnoreCase("png")) { Runnable runnable = () -> { - processSourceFile(files.first(), true); + Gdx.app.postRunnable(() -> { + processSourceFile(files.first(), true); + }); }; main.getDialogFactory().showDialogLoading(runnable); @@ -132,6 +135,7 @@ public DialogImageFont(Main main, ImageFontListener imageFontListener) { var textButton = new TextButton("Generate Font", skin); textButton.setName("generate"); textButton.setDisabled(true); + updateLabelHighlight("image-label"); button(textButton, true); textButton.addListener(main.getHandListener()); textButton.addListener(new TextTooltip("Generate bitmap font, save to specified file, and add to list of fonts", main.getTooltipManager(), skin)); @@ -140,9 +144,11 @@ public DialogImageFont(Main main, ImageFontListener imageFontListener) { @Override public boolean keyDown(InputEvent event, int keycode) { if (keycode == Keys.ENTER) { - var textButton = ((TextButton) findActor("generate")); - if (!textButton.isDisabled()) { - textButton.fire(new ChangeListener.ChangeEvent()); + if (getStage().getKeyboardFocus() != findActor("preview")) { + var textButton = ((TextButton) findActor("generate")); + if (!textButton.isDisabled()) { + textButton.fire(new ChangeListener.ChangeEvent()); + } } } return false; @@ -217,7 +223,7 @@ protected void result(Object object) { private void refreshTable() { previewStyle = new TextFieldStyle(skin.get(TextFieldStyle.class)); - fadables = new Array(); + fadables = new Array<>(); root.clearChildren(); var scrollTable = new Table(); @@ -250,12 +256,7 @@ private void refreshTable() { table.add(textField).growX(); textField.addListener(new TextTooltip("Characters to be included in font", main.getTooltipManager(), skin)); textField.addListener(main.getIbeamListener()); - textField.setTextFieldFilter(new TextField.TextFieldFilter() { - @Override - public boolean acceptChar(TextField textField, char c) { - return textField.getText().indexOf(c) == -1; - } - }); + textField.setTextFieldFilter((TextField textField1, char c) -> textField1.getText().indexOf(c) == -1); textField.addListener(new ChangeListener() { @Override @@ -266,22 +267,29 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { }); var selectBox = new SelectBox(skin); - selectBox.setItems("0-9", "a-zA-Z", "a-zA-Z0-9", "a-zA-Z0-9!-?*", "custom"); - if (settings.characters.equals(NUMBERS)) { - selectBox.setSelectedIndex(0); - } else if (settings.characters.equals(ALPHA)) { - selectBox.setSelectedIndex(1); - } else if (settings.characters.equals(ALPHA_NUMERIC)) { - selectBox.setSelectedIndex(2); - } else if (settings.characters.equals(ALL)) { - selectBox.setSelectedIndex(3); - } else { - selectBox.setSelectedIndex(4); + selectBox.setItems("0-9", "a-zA-Z", "a-zA-Z0-9", "a-zA-Z0-9!-?*", "custom", "Load from file (UTF-8)..."); + switch (settings.characters) { + case NUMBERS: + selectBox.setSelectedIndex(0); + break; + case ALPHA: + selectBox.setSelectedIndex(1); + break; + case ALPHA_NUMERIC: + selectBox.setSelectedIndex(2); + break; + case ALL: + selectBox.setSelectedIndex(3); + break; + default: + selectBox.setSelectedIndex(4); + break; } selectBox.setName("characters select"); table.add(selectBox); selectBox.addListener(new TextTooltip("Character Presets", main.getTooltipManager(), skin)); selectBox.addListener(main.getHandListener()); + selectBox.getList().addListener(main.getHandListener()); selectBox.addListener(new ChangeListener() { @Override public void changed(ChangeListener.ChangeEvent event, Actor actor) { @@ -289,18 +297,36 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { switch (selectBox.getSelectedIndex()) { case 0: textField.setText(NUMBERS); + textField.setMessageText(""); + settings.characters = NUMBERS; break; case 1: textField.setText(ALPHA); + textField.setMessageText(""); + settings.characters = ALPHA; break; case 2: textField.setText(ALPHA_NUMERIC); + textField.setMessageText(""); + settings.characters = ALPHA_NUMERIC; break; case 3: textField.setText(ALL); + textField.setMessageText(""); + settings.characters = ALL; + break; + case 4: + textField.setText(textField.getText()); + textField.setMessageText(""); + settings.characters = ((TextField) findActor("characters")).getText(); + break; + case 5: + textField.setText(""); + textField.setMessageText("Characters loaded from text file..."); + settings.characters = ""; + showCharacterDialog(); break; } - settings.characters = ((TextField) findActor("characters")).getText(); } }); @@ -326,6 +352,7 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { //source file content.row(); label = new Label("Image File", skin); + label.setName("image-label"); content.add(label).right(); width = label.getWidth(); @@ -608,7 +635,7 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { public void changed(ChangeListener.ChangeEvent event, Actor actor) { try { Utils.openFileExplorer(Main.appFolder.child("imagefont/characters/")); - } catch (Exception e) { + } catch (IOException e) { Gdx.app.error(getClass().getName(), "Error opening characters folder", e); main.getDialogFactory().showDialogError("Folder Error...", "Error opening characters folder.\n\nOpen log?"); } @@ -656,6 +683,7 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { if (colorData != null) { var textArea = (TextArea) findActor("preview"); previewStyle.background = ((NinePatchDrawable) previewStyle.background).tint(colorData.color); + previewStyle.focusedBackground = ((NinePatchDrawable) previewStyle.focusedBackground).tint(colorData.color); settings.previewBackgroundColor = colorData.color; } }, null); @@ -681,8 +709,10 @@ private void sourceFileBrowse() { File file = main.getDesktopWorker().openDialog("Select image file...", defaultPath, filterPatterns, "Image files"); if (file != null) { - FileHandle fileHandle = new FileHandle(file); - processSourceFile(fileHandle, true); + Gdx.app.postRunnable(() -> { + FileHandle fileHandle = new FileHandle(file); + processSourceFile(fileHandle, true); + }); } }; @@ -704,6 +734,7 @@ private void processSourceFile(FileHandle fileHandle, boolean setDefaults) { textField.setCursorPosition(textField.getText().length() - 1); ((TextButton) findActor("generate")).setDisabled(false); + updateLabelHighlight(null); for (var fadable : fadables) { fadable.addAction(Actions.fadeIn(1.0f, Interpolation.fade)); @@ -719,30 +750,32 @@ private void processSourceFile(FileHandle fileHandle, boolean setDefaults) { private void saveFileBrowse() { Runnable runnable = () -> { - var textField = (TextField) findActor("targetpath"); + Gdx.app.postRunnable(() -> { + var textField = (TextField) findActor("targetpath"); - String defaultPath = main.getProjectData().getLastFontPath(); - FileHandle currentPath = Gdx.files.absolute(textField.getText()); - if (currentPath.exists()) { - defaultPath = textField.getText(); - } + String defaultPath = main.getProjectData().getLastFontPath(); + FileHandle currentPath = Gdx.files.absolute(textField.getText()); + if (currentPath.exists()) { + defaultPath = textField.getText(); + } - String[] filterPatterns = null; - if (!Utils.isMac()) { - filterPatterns = new String[]{"*.fnt"}; - } + String[] filterPatterns = null; + if (!Utils.isMac()) { + filterPatterns = new String[]{"*.fnt"}; + } - File file = main.getDesktopWorker().saveDialog("Save as font file...", defaultPath, filterPatterns, "Font files"); - if (file != null) { - var fileHandle = new FileHandle(file); - if (!fileHandle.extension().equalsIgnoreCase("fnt")) { - fileHandle = fileHandle.parent().child(fileHandle.name() + ".fnt"); + File file = main.getDesktopWorker().saveDialog("Save as font file...", defaultPath, filterPatterns, "Font files"); + if (file != null) { + var fileHandle = new FileHandle(file); + if (!fileHandle.extension().equalsIgnoreCase("fnt")) { + fileHandle = fileHandle.parent().child(fileHandle.name() + ".fnt"); + } + + processSaveFile(fileHandle.path()); + + main.getProjectData().setLastFontPath(fileHandle.parent().path() + "/"); } - - processSaveFile(fileHandle.path()); - - main.getProjectData().setLastFontPath(fileHandle.parent().path() + "/"); - } + }); }; main.getDialogFactory().showDialogLoading(runnable); @@ -755,6 +788,7 @@ private void processSaveFile(String path) { textField.setCursorPosition(textField.getText().length() - 1); ((TextButton) findActor("generate")).setDisabled(false); + updateLabelHighlight(null); } private void saveSettingsBrowse() { @@ -768,14 +802,16 @@ private void saveSettingsBrowse() { File file = main.getDesktopWorker().saveDialog("Save Image Font Settings...", defaultPath, filterPatterns, "Imagefont files"); if (file != null) { - var fileHandle = new FileHandle(file); - if (!fileHandle.extension().equalsIgnoreCase("imagefont")) { - fileHandle = fileHandle.parent().child(fileHandle.name() + ".imagefont"); - } - - saveSettings(fileHandle); - - main.getProjectData().setLastFontPath(fileHandle.parent().path() + "/"); + Gdx.app.postRunnable(() -> { + var fileHandle = new FileHandle(file); + if (!fileHandle.extension().equalsIgnoreCase("imagefont")) { + fileHandle = fileHandle.parent().child(fileHandle.name() + ".imagefont"); + } + + saveSettings(fileHandle); + + main.getProjectData().setLastFontPath(fileHandle.parent().path() + "/"); + }); } }; @@ -815,23 +851,25 @@ public ImageFontSettings() { } private void saveSettings(FileHandle file) { - file.writeString(json.prettyPrint(settings), false); + file.writeString(json.prettyPrint(settings), false, "utf-8"); } private void loadSettingsBrowse() { Runnable runnable = () -> { - String defaultPath = main.getProjectData().getLastFontPath(); + Gdx.app.postRunnable(() -> { + String defaultPath = main.getProjectData().getLastFontPath(); - String[] filterPatterns = null; - if (!Utils.isMac()) { - filterPatterns = new String[]{"*.imagefont"}; - } + String[] filterPatterns = null; + if (!Utils.isMac()) { + filterPatterns = new String[]{"*.imagefont"}; + } - File file = main.getDesktopWorker().openDialog("Load Image Font Settings...", defaultPath, filterPatterns, "Imagefont files"); - if (file != null) { - FileHandle fileHandle = new FileHandle(file); - loadSettings(fileHandle); - } + File file = main.getDesktopWorker().openDialog("Load Image Font Settings...", defaultPath, filterPatterns, "Imagefont files"); + if (file != null) { + FileHandle fileHandle = new FileHandle(file); + loadSettings(fileHandle); + } + }); }; main.getDialogFactory().showDialogLoading(runnable); @@ -842,10 +880,11 @@ private void loadSettings(FileHandle file) { var targetPath = ((TextField) findActor("targetpath")).getText(); json.setOutputType(JsonWriter.OutputType.json); - settings = json.fromJson(ImageFontSettings.class, file); + settings = json.fromJson(ImageFontSettings.class, file.readString("utf-8")); refreshTable(); previewStyle.fontColor = settings.previewColor; previewStyle.background = ((NinePatchDrawable) previewStyle.background).tint(settings.previewBackgroundColor); + previewStyle.focusedBackground = ((NinePatchDrawable) previewStyle.focusedBackground).tint(settings.previewBackgroundColor); if (targetPath != null && !targetPath.equals("")) { processSaveFile(targetPath); @@ -1075,7 +1114,6 @@ private void loadPixmap(FileHandle fileHandle, boolean setDefaults) throws Inval } if (!failure && lookingForBreak) { - lookingForBreak = false; bitmapCharacter.width = fontPixmap.getWidth() - 1 - bitmapCharacter.x; averageWidth += bitmapCharacter.width; bitmapCharacters.add(bitmapCharacter); @@ -1125,12 +1163,22 @@ private void loadPixmap(FileHandle fileHandle, boolean setDefaults) throws Inval if (!lineEmpty) { character.cropHeight = y - character.cropY + 1; character.yoffset = character.cropY - character.y; - character.baseline = y - character.y; + character.baseline = y - character.cropY; break; } } } + //find baseline + var baseline = 0; + for (var character : bitmapCharacters) { + if (!BASELINE_EXCLUSION.contains(character.character)) { + if (character.baseline > baseline) { + baseline = character.baseline; + } + } + } + //write characters to temporary PNGs Main.appFolder.child("imagefont/characters").emptyDirectory(); for (var character : bitmapCharacters) { @@ -1248,21 +1296,10 @@ private void loadPixmap(FileHandle fileHandle, boolean setDefaults) throws Inval ((Spinner) findActor("leading")).setValue(height); settings.leading = height; - var baseline = 0; - var baselineCount = 0; - - for (var bitmapCharacter : bitmapCharacters) { - if (!BASELINE_EXCLUSION.contains(bitmapCharacter.character) && Character.toString(bitmapCharacter.character).matches("[A-Z]+|\\d+")) { - baseline += bitmapCharacter.baseline; - baselineCount++; - } - } - - if (baselineCount == 0) { + if (baseline == 0) { baseline = settings.leading; - } else { - baseline = baseline / baselineCount + 1; } + ((Spinner) findActor("baseline")).setValue(baseline); settings.baseline = baseline; @@ -1306,12 +1343,7 @@ public KerningPair(char character1, char character2, int value) { private void writeFNT(FileHandle saveFile, boolean texturePack) { if (texturePack) { //delete existing PNG's - var deleteFiles = saveFile.parent().list(new FilenameFilter() { - @Override - public boolean accept(File dir, String name) { - return name.matches(saveFile.nameWithoutExtension() + "\\d*?\\.png|" + saveFile.nameWithoutExtension() + "\\.atlas|" + saveFile.nameWithoutExtension() + "\\.fnt"); - } - }); + var deleteFiles = saveFile.parent().list((File dir, String name1) -> name1.matches(saveFile.nameWithoutExtension() + "\\d*?\\.png|" + saveFile.nameWithoutExtension() + "\\.atlas|" + saveFile.nameWithoutExtension() + "\\.fnt")); for (var file : deleteFiles) { file.delete(); } @@ -1429,4 +1461,55 @@ private String sanitizeFileName(String name) { name = name.replace("|", "pipe"); return name; } + + private void updateLabelHighlight(String requiredLabelName) { + var normalStyle = skin.get(Label.LabelStyle.class); + var requiredStyle = skin.get("required", Label.LabelStyle.class); + var actors = new Array(); + actors.addAll(getChildren()); + + for (int i = 0; i < actors.size; i++) { + var actor = actors.get(i); + + if (actor instanceof Group) { + actors.addAll(((Group) actor).getChildren()); + } + + if (actor instanceof Label) { + Label label = (Label) actor; + + if (label.getStyle().equals(requiredStyle)) { + label.setStyle(normalStyle); + } + + if (requiredLabelName != null && label.getName() != null && label.getName().equals(requiredLabelName)) { + label.setStyle(requiredStyle); + } + } + } + } + + private void showCharacterDialog() { + Runnable runnable = () -> { + String defaultPath = main.getProjectData().getLastFontPath(); + + File file = main.getDesktopWorker().openDialog("Select character text file...", defaultPath, null, "All files"); + if (file != null) { + var fileHandle = new FileHandle(file); + String characters = fileHandle.readString("utf-8"); + Gdx.app.postRunnable(() -> { + settings.characters = Utils.removeDuplicateCharacters(characters); + var textField = (TextField) findActor("characters"); + textField.setText(""); + }); + } else { + Gdx.app.postRunnable(() -> { + SelectBox selectBox = findActor("characters select"); + selectBox.setSelectedIndex(3); + }); + } + }; + + main.getDialogFactory().showDialogLoading(runnable); + } } \ No newline at end of file diff --git a/core/src/com/ray3k/skincomposer/dialog/DialogImport.java b/core/src/com/ray3k/skincomposer/dialog/DialogImport.java index 3422974a..33c5d3da 100644 --- a/core/src/com/ray3k/skincomposer/dialog/DialogImport.java +++ b/core/src/com/ray3k/skincomposer/dialog/DialogImport.java @@ -37,6 +37,8 @@ import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; import com.badlogic.gdx.utils.Array; import com.ray3k.skincomposer.Main; +import com.ray3k.skincomposer.utils.Utils; + import java.nio.file.Paths; /** @@ -160,8 +162,10 @@ private void newFile(FileHandle fileHandle) { (int selection) -> { if (selection == 0) { main.getMainListener().saveFile(() -> { - main.getProjectData().clear(); - importFile(fileHandle); + Gdx.app.postRunnable(() -> { + main.getProjectData().clear(); + importFile(fileHandle); + }); }); } else if (selection == 1) { main.getProjectData().clear(); @@ -176,23 +180,25 @@ private void newFile(FileHandle fileHandle) { private void importFile(FileHandle fileHandle) { main.getDialogFactory().showDialogLoading(() -> { - Array warnings = new Array<>(); + Gdx.app.postRunnable(() -> { + Array warnings = new Array<>(); - try { - Array newWarnings = main.getJsonData().readFile(fileHandle); - warnings.addAll(newWarnings); - main.getProjectData().getAtlasData().atlasCurrent = false; - main.getJsonData().checkForPropertyConsistency(); - main.getRootTable().produceAtlas(); - main.getRootTable().populate(); - } catch (Exception e) { - Gdx.app.error(getClass().getName(), "Error attempting to import JSON", e); - main.getDialogFactory().showDialogError("Import Error...", "Error while attempting to import a skin.\nPlease check that all files exist.\n\nOpen log?"); - } + try { + Array newWarnings = main.getJsonData().readFile(fileHandle); + warnings.addAll(newWarnings); + main.getProjectData().getAtlasData().atlasCurrent = false; + main.getJsonData().checkForPropertyConsistency(); + main.getRootTable().produceAtlas(); + main.getRootTable().populate(); + } catch (Exception e) { + Gdx.app.error(getClass().getName(), "Error attempting to import JSON", e); + main.getDialogFactory().showDialogError("Import Error...", "Error while attempting to import a skin.\nPlease check that all files exist.\n\nOpen log?"); + } - if (warnings.size > 0) { - main.getDialogFactory().showWarningDialog(warnings); - } + if (warnings.size > 0) { + main.getDialogFactory().showWarningDialog(warnings); + } + }); }); } @@ -209,7 +215,10 @@ public boolean remove() { } private void showFileBrowser() { - String[] filterPatterns = {"*.json"}; + String[] filterPatterns = null; + if (!Utils.isMac()) { + filterPatterns = new String[] {"*.json"}; + } TextField textField = findActor("path"); var file = main.getDesktopWorker().openDialog("Import skin...", textField.getText(), filterPatterns, "Json files"); diff --git a/core/src/com/ray3k/skincomposer/dialog/DialogLoading.java b/core/src/com/ray3k/skincomposer/dialog/DialogLoading.java index 94bd4294..de7d4df1 100644 --- a/core/src/com/ray3k/skincomposer/dialog/DialogLoading.java +++ b/core/src/com/ray3k/skincomposer/dialog/DialogLoading.java @@ -23,6 +23,7 @@ ******************************************************************************/ package com.ray3k.skincomposer.dialog; +import com.badlogic.gdx.Gdx; import com.badlogic.gdx.scenes.scene2d.Action; import com.badlogic.gdx.scenes.scene2d.Stage; import com.badlogic.gdx.scenes.scene2d.actions.DelayAction; @@ -51,10 +52,15 @@ public Dialog show(Stage stage) { Dialog dialog = super.show(stage); RunnableAction runnableAction = new RunnableAction(); runnableAction.setRunnable(() -> { - if (runnable != null) { - runnable.run(); - } - hide(); + Thread thread = new Thread(() -> { + if (runnable != null) { + runnable.run(); + } + Gdx.app.postRunnable(() -> { + hide(); + }); + }); + thread.start(); }); Action action = new SequenceAction(new DelayAction(.5f), runnableAction); addAction(action); diff --git a/core/src/com/ray3k/skincomposer/dialog/DialogPathErrors.java b/core/src/com/ray3k/skincomposer/dialog/DialogPathErrors.java index 526757a9..7b4e93bd 100644 --- a/core/src/com/ray3k/skincomposer/dialog/DialogPathErrors.java +++ b/core/src/com/ray3k/skincomposer/dialog/DialogPathErrors.java @@ -142,8 +142,10 @@ public void changed(ChangeListener.ChangeEvent event, if (!Utils.isMac()) { filterPatterns = new String[] {"*.png", "*.jpg", "*.jpeg", "*.bmp", "*.gif"}; } + + var defaultPath = drawable.file.parent().exists() ? drawable.file.parent().path() + "/": ""; - File file = desktopWorker.openDialog("Locate " + drawable.file.name() + "...", drawable.file.path(), filterPatterns, "Image files"); + File file = desktopWorker.openDialog("Locate " + drawable.file.name() + "...", defaultPath, filterPatterns, "Image files"); if (file != null) { FileHandle fileHandle = new FileHandle(file); drawable.file = fileHandle; @@ -215,8 +217,10 @@ public void changed(ChangeListener.ChangeEvent event, if (!Utils.isMac()) { filterPatterns = new String[] {"*.fnt"}; } + + var defaultPath = font.file.parent().exists() ? font.file.parent().path() + "/": ""; - File file = desktopWorker.openDialog("Locate " + font.file.name() + "...", font.file.path(), filterPatterns, "Font files"); + File file = desktopWorker.openDialog("Locate " + font.file.name() + "...", defaultPath, filterPatterns, "Font files"); if (file != null) { FileHandle fileHandle = new FileHandle(file); font.file = fileHandle; @@ -244,6 +248,9 @@ public void changed(ChangeListener.ChangeEvent event, dataTable.add(image).colspan(4).pad(5.0f).padLeft(0.0f).padRight(0.0f).growX(); } } + + dataTable.row(); + dataTable.add().grow().colspan(4); } private void resolveAssetsFromFolder(FileHandle folder, Array drawables, Array fonts) { diff --git a/core/src/com/ray3k/skincomposer/dialog/DialogSettings.java b/core/src/com/ray3k/skincomposer/dialog/DialogSettings.java index ebbdffbc..e8e4d9e3 100644 --- a/core/src/com/ray3k/skincomposer/dialog/DialogSettings.java +++ b/core/src/com/ray3k/skincomposer/dialog/DialogSettings.java @@ -45,6 +45,7 @@ public class DialogSettings extends Dialog { private Integer maxUndos; private boolean resourcesRelative; private boolean allowingWelcome; + private boolean exportWarnings; private boolean allowingUpdates; private final Main main; @@ -55,6 +56,7 @@ public DialogSettings(String title, String windowStyleName, Main main) { maxUndos = main.getProjectData().getMaxUndos(); resourcesRelative = main.getProjectData().areResourcesRelative(); allowingWelcome = main.getProjectData().isAllowingWelcome(); + exportWarnings = main.getProjectData().isShowingExportWarnings(); allowingUpdates = main.getProjectData().isCheckingForUpdates(); populate(); @@ -69,6 +71,7 @@ protected void result(Object object) { main.getProjectData().setMaxUndos(maxUndos); main.getProjectData().setResourcesRelative(resourcesRelative); main.getProjectData().setAllowingWelcome(allowingWelcome); + main.getProjectData().setShowingExportWarnings(exportWarnings); main.getProjectData().setCheckingForUpdates(allowingUpdates); main.getUndoableManager().clearUndoables(); @@ -163,10 +166,10 @@ public void changed(ChangeListener.ChangeEvent event, @Override public void changed(ChangeListener.ChangeEvent event, Actor actor) { try { - Utils.openFileExplorer(Main.appFolder.child("texturepacker/defaults.json")); + Utils.openFileExplorer(Main.appFolder.child("texturepacker/atlas-export-settings.json")); } catch (Exception e) { - Gdx.app.error(getClass().getName(), "Error opening defaults.json", e); - main.getDialogFactory().showDialogError("File Error...", "Error opening defaults.json\n\nOpen log?"); + Gdx.app.error(getClass().getName(), "Error opening atlas-export-settings.json", e); + main.getDialogFactory().showDialogError("File Error...", "Error opening atlas-export-settings.json\n\nOpen log?"); } } }); @@ -231,6 +234,18 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { }); table.add(welcomeCheckBox); + table.row(); + var exportWarningsCheckBox = new ImageTextButton("Show export warnings?", getSkin(), "checkbox"); + exportWarningsCheckBox.setChecked(exportWarnings); + exportWarningsCheckBox.addListener(main.getHandListener()); + exportWarningsCheckBox.addListener(new ChangeListener() { + @Override + public void changed(ChangeListener.ChangeEvent event, Actor actor) { + exportWarnings = exportWarningsCheckBox.isChecked(); + } + }); + table.add(exportWarningsCheckBox); + table.row(); var updatesCheckBox = new ImageTextButton("Check for updates?", getSkin(), "checkbox"); updatesCheckBox.setChecked(allowingUpdates); diff --git a/core/src/com/ray3k/skincomposer/dialog/DialogWarnings.java b/core/src/com/ray3k/skincomposer/dialog/DialogWarnings.java index d8f77b59..fc60e85f 100644 --- a/core/src/com/ray3k/skincomposer/dialog/DialogWarnings.java +++ b/core/src/com/ray3k/skincomposer/dialog/DialogWarnings.java @@ -33,6 +33,7 @@ import com.badlogic.gdx.scenes.scene2d.EventListener; import com.badlogic.gdx.scenes.scene2d.Stage; import com.badlogic.gdx.scenes.scene2d.ui.Button; +import com.badlogic.gdx.scenes.scene2d.ui.CheckBox; import com.badlogic.gdx.scenes.scene2d.ui.Dialog; import com.badlogic.gdx.scenes.scene2d.ui.Label; import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane; @@ -72,6 +73,8 @@ public DialogWarnings(Main main, Array warnings) { setMovable(false); + getContentTable().pad(5); + Button button = new Button(getSkin(), "close"); button.addListener(main.getHandListener()); button.addListener(new ChangeListener() { @@ -88,7 +91,7 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { getContentTable().row(); Table table = new Table(); - table.pad(10.0f).padTop(0.0f); + table.pad(5.0f).padTop(0.0f); table.defaults().space(10.0f); scrollPane = new ScrollPane(table, main.getSkin()); scrollPane.setFadeScrollBars(false); @@ -96,6 +99,18 @@ public void changed(ChangeListener.ChangeEvent event, Actor actor) { scrollPane.setScrollingDisabled(true, false); getContentTable().add(scrollPane).grow(); + getContentTable().row(); + var checkBox = new CheckBox("Do not show again", getSkin()); + checkBox.setChecked(!main.getProjectData().isShowingExportWarnings()); + getContentTable().add(checkBox).right(); + checkBox.addListener(main.getHandListener()); + checkBox.addListener(new ChangeListener() { + @Override + public void changed(ChangeListener.ChangeEvent event, Actor actor) { + main.getProjectData().setShowingExportWarnings(!checkBox.isChecked()); + } + }); + for (String warning : warnings) { table.row(); label = new Label(warning, getSkin()); diff --git a/core/src/com/ray3k/skincomposer/utils/Utils.java b/core/src/com/ray3k/skincomposer/utils/Utils.java index eb4124c2..13e303b7 100644 --- a/core/src/com/ray3k/skincomposer/utils/Utils.java +++ b/core/src/com/ray3k/skincomposer/utils/Utils.java @@ -38,12 +38,13 @@ import java.io.IOException; import java.io.InputStream; import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.Set; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import javax.imageio.ImageIO; import javax.imageio.ImageReader; import javax.imageio.stream.FileImageInputStream; -import javax.imageio.stream.ImageInputStream; public class Utils { public static String os; @@ -269,8 +270,7 @@ public static boolean doesImageFitBox(FileHandle fileHandle, float width, float Iterator iter = ImageIO.getImageReadersBySuffix(suffix); if (iter.hasNext()) { ImageReader reader = iter.next(); - try { - ImageInputStream stream = new FileImageInputStream(fileHandle.file()); + try (var stream = new FileImageInputStream(fileHandle.file())) { reader.setInput(stream); int imageWidth = reader.getWidth(reader.getMinIndex()); int imageHeight = reader.getHeight(reader.getMinIndex()); @@ -372,4 +372,18 @@ public static Cursor textureRegionToCursor(TextureRegion textureRegion, int xHot public static int colorToInt(Color color) { return ((int)(255 * color.r) << 24) | ((int)(255 * color.g) << 16) | ((int)(255 * color.b) << 8) | ((int)(255 * color.a)); } + + public static String removeDuplicateCharacters(String string) { + char[] chars = string.toCharArray(); + Set charSet = new LinkedHashSet(); + for (char c : chars) { + charSet.add(c); + } + + var sb = new StringBuilder(); + charSet.forEach((character) -> { + sb.append(character); + }); + return sb.toString(); + } } diff --git a/desktop/build.gradle b/desktop/build.gradle index 54faf3fb..b4610669 100644 --- a/desktop/build.gradle +++ b/desktop/build.gradle @@ -1,13 +1,19 @@ apply plugin: "java" -sourceCompatibility = 1.10 +sourceCompatibility = 1.8 sourceSets.main.java.srcDirs = [ "src/" ] project.ext.mainClassName = "com.ray3k.skincomposer.desktop.DesktopLauncher" -project.ext.assetsDir = new File("../core/assets"); +project.ext.mainClassNameMac = "com.ray3k.skincomposer.desktop.MacLauncher" +project.ext.assetsDir = new File("../core/assets") +def osName = System.getProperty('os.name').toLowerCase(Locale.ROOT) task run(dependsOn: classes, type: JavaExec) { - main = project.mainClassName + if (osName.contains('mac')) { + main = project.mainClassNameMac + } else { + main = project.mainClassName + } classpath = sourceSets.main.runtimeClasspath standardInput = System.in workingDir = project.assetsDir @@ -15,7 +21,11 @@ task run(dependsOn: classes, type: JavaExec) { } task debug(dependsOn: classes, type: JavaExec) { - main = project.mainClassName + if (osName.contains('mac')) { + main = project.mainClassNameMac + } else { + main = project.mainClassName + } classpath = sourceSets.main.runtimeClasspath standardInput = System.in workingDir = project.assetsDir @@ -24,59 +34,75 @@ task debug(dependsOn: classes, type: JavaExec) { } task dist(type: Jar) { - archiveName = "skin_composer.jar"; + archiveName = "${project.appName}.jar"; from files(sourceSets.main.output.classesDir) from files(sourceSets.main.output.resourcesDir) from {configurations.compile.collect {zipTree(it)}} from files(project.assetsDir); - manifest { - attributes ('Main-Class': project.mainClassName, 'SplashScreen-Image' : 'splash.png') + if (osName.contains('mac')) { + manifest { + attributes ('Main-Class': project.mainClassNameMac) + } + } else { + manifest { + attributes ('Main-Class': project.mainClassName, 'SplashScreen-Image' : 'splash.png') + } } } dist.dependsOn classes // creates application bundle (executable + runtime) -task javaPackager(type: Exec, dependsOn: dist) { +task jpackageStart(type: Exec, dependsOn: dist) { workingDir project.projectDir def commands = [ - 'javapackager', - '-deploy', - '-nosign', - '-outdir', "${buildDir}/distribution", - '-srcdir', "${buildDir}/libs", - '-native', 'image', - '-name', project.appName, - '-appclass', project.mainClassName + "${project.projectDir}/jpackage/bin/jpackage", + 'create-image', + '--output', "${buildDir}/distribution", + '--input', "${buildDir}/libs", + '--name', project.appName, + '--main-class', project.mainClassName, + '--main-jar', "${project.appName}.jar", + '--overwrite' ] - def osName = System.getProperty('os.name').toLowerCase(Locale.ROOT) if (osName.contains('windows')) { - commands << "-Bicon=${project.projectDir}/logo.ico" - commands << "-BjvmOptions=-splash:splash.png" + commands << '--icon' + commands << "${project.projectDir}/logo.ico" + commands << '--jvm-args' + commands << "-splash:splash.png" } else if (osName.contains('linux')) { - commands << "-Bicon=${project.projectDir}/logo.png" - commands << "-BjvmOptions=-splash:splash.png" + commands << '--icon' + commands << "${project.projectDir}/logo.png" + commands << '--jvm-args' + commands << "-splash:splash.png" } else if (osName.contains('mac')) { - commands << "-Bicon=${project.projectDir}/logo.icns" - commands << "-BjvmOptions=-XstartOnFirstThread" + commands << '--icon' + commands << "${project.projectDir}/logo.icns" + commands << '--jvm-args' + commands << "-XstartOnFirstThread" } commandLine = commands doLast() { - copy { - from file("${project.projectDir}/splash.png"); - into file("${buildDir}/distribution/${project.appName}/app"); + if (!osName.contains('mac')) { + copy { + from '../core/assets/splash.png' + into "${buildDir}/distribution/${project.appName}/app" + } } } } // removes bloated runtime created by javapackager -task cleanPackagerRuntime(dependsOn: javaPackager) { +task jpackageCleanRuntime(dependsOn: jpackageStart) { doLast() { File runtimeFile = new File("${buildDir}/distribution/${project.appName}/runtime") + if (osName.contains('mac')) { + runtimeFile = new File("${buildDir}/distribution/${project.appName}.app/contents/plugins/Java.runtime/Contents/Home") + } runtimeFile.deleteDir() delete { delete fileTree("${buildDir}/distribution/${project.appName}").matching { @@ -88,13 +114,16 @@ task cleanPackagerRuntime(dependsOn: javaPackager) { } // creates a replacement runtime via jlink command (much smaller than jpackager) -task createFinalAppBundle(type: Exec, dependsOn: [cleanPackagerRuntime]) { - def jdk = System.getProperty("java.home") +task jpackageAppBundle(type: Exec, dependsOn: [jpackageCleanRuntime]) { + String runtimePath = "${buildDir}/distribution/${project.appName}/runtime" + if (osName.contains('mac')) { + runtimePath = "${buildDir}/distribution/${project.appName}.app/contents/plugins/Java.runtime/Contents/Home" + } workingDir project.projectDir commandLine = [ - 'jlink', - '-p', "${jdk}/jmods", + "${project.projectDir}/jpackage/bin/jlink", + '-p', "${project.projectDir}/jpackage/jmods", '--add-modules', 'java.base,java.desktop,jdk.unsupported', '--strip-debug', '--no-header-files', @@ -102,9 +131,9 @@ task createFinalAppBundle(type: Exec, dependsOn: [cleanPackagerRuntime]) { '--strip-native-commands', "--vm=server", "--compress=2", - '--output', "${buildDir}/distribution/${project.appName}/runtime" + '--output', runtimePath ] - + doLast{ System.out.println("Application '${project.appName}' packaged.") System.out.println(" -> location: ${buildDir}/distribution/${project.appName}/") @@ -132,9 +161,14 @@ task updateAndCreateZip(type: Zip, dependsOn: [dist]) { from '../core/assets/splash.png' into "${buildDir}/distribution/${project.appName}/app" } + + copy { + from './samples/' + into "${buildDir}/distribution/${project.appName}/samples/" + } } - archiveName "skin-composer-${org.gradle.internal.os.OperatingSystem.current().getFamilyName()}.zip" + archiveName "${project.appName}_${org.gradle.internal.os.OperatingSystem.current().getFamilyName()}.zip" from "${buildDir}/distribution/${project.appName}" destinationDir = file("${buildDir}/distribution/") } diff --git a/desktop/splash.png b/desktop/splash.png deleted file mode 100644 index 2ce2d1b7..00000000 Binary files a/desktop/splash.png and /dev/null differ diff --git a/desktop/src/com/ray3k/skincomposer/desktop/DesktopLauncher.java b/desktop/src/com/ray3k/skincomposer/desktop/DesktopLauncher.java index 184b7247..259e3b78 100644 --- a/desktop/src/com/ray3k/skincomposer/desktop/DesktopLauncher.java +++ b/desktop/src/com/ray3k/skincomposer/desktop/DesktopLauncher.java @@ -1,38 +1,16 @@ package com.ray3k.skincomposer.desktop; import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.Graphics; -import com.badlogic.gdx.backends.lwjgl3.*; -import com.badlogic.gdx.files.FileHandle; -import com.badlogic.gdx.graphics.Pixmap; -import com.badlogic.gdx.graphics.Texture; -import com.badlogic.gdx.graphics.g2d.PixmapPacker; -import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator; -import com.badlogic.gdx.tools.bmfont.BitmapFontWriter; -import com.badlogic.gdx.tools.texturepacker.TexturePacker; -import com.badlogic.gdx.tools.texturepacker.TexturePacker.Settings; -import com.badlogic.gdx.utils.Array; -import com.badlogic.gdx.utils.Json; -import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.graphics.PixmapIO; -import com.ray3k.skincomposer.*; +import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application; +import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration; +import com.ray3k.skincomposer.Main; import com.ray3k.skincomposer.utils.Utils; -import org.lwjgl.PointerBuffer; -import org.lwjgl.system.MemoryStack; -import javax.swing.*; -import java.awt.*; -import java.io.File; +import java.awt.SplashScreen; import java.io.FileWriter; import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.List; +import javax.swing.JOptionPane; -import static org.lwjgl.system.MemoryStack.stackPush; - -public class DesktopLauncher implements DesktopWorker, Lwjgl3WindowListener { - private Array filesDroppedListeners; - private CloseListener closeListener; - +public class DesktopLauncher extends Launcher { public static void main(String[] args) { Lwjgl3ApplicationConfiguration config = new Lwjgl3ApplicationConfiguration(); config.setResizable(true); @@ -70,259 +48,7 @@ public static void main(String[] args) { } } } - - public DesktopLauncher() { - filesDroppedListeners = new Array<>(); - } - - @Override - public void texturePack(Array handles, FileHandle localFile, FileHandle targetFile) { - //copy defaults.json to temp folder if it doesn't exist - FileHandle fileHandle = Gdx.files.external(".skincomposer/texturepacker/defaults.json"); - if (!fileHandle.exists()) { - Gdx.files.internal("defaults.json").copyTo(fileHandle); - } - - Json json = new Json(); - Settings settings = json.fromJson(Settings.class, fileHandle); - - TexturePacker p = new TexturePacker(settings); - for (FileHandle handle : handles) { - if (handle.exists()) { - p.addImage(handle.file()); - } else { - if (localFile != null) { - FileHandle localHandle = localFile.sibling(localFile.nameWithoutExtension() + "_data/" + handle.name()); - if (localHandle.exists()) { - p.addImage(localHandle.file()); - } else { - Gdx.app.error(getClass().getName(), "File does not exist error while creating texture atlas: " + handle.path()); - } - } else { - Gdx.app.error(getClass().getName(), "File does not exist error while creating texture atlas: " + handle.path()); - } - } - } - p.pack(targetFile.parent().file(), targetFile.nameWithoutExtension()); - } - - @Override - public void packFontImages(Array files, FileHandle saveFile) { - var settings = new Settings(); - settings.pot = false; - settings.duplicatePadding = true; - settings.filterMin = Texture.TextureFilter.Linear; - settings.filterMag = Texture.TextureFilter.Linear; - settings.ignoreBlankImages = false; - settings.useIndexes = false; - settings.limitMemory = false; - settings.maxWidth = 2048; - settings.maxHeight = 2048; - settings.flattenPaths = true; - settings.silent = true; - var texturePacker = new TexturePacker(settings); - - for (var file : files) { - if (file.exists()) { - texturePacker.addImage(file.file()); - } - } - - texturePacker.pack(saveFile.parent().file(), saveFile.nameWithoutExtension()); - } - - @Override - public void centerWindow(Graphics graphics) { - Lwjgl3Graphics g = (Lwjgl3Graphics) graphics; - Graphics.DisplayMode mode = g.getDisplayMode(); - Lwjgl3Window window = g.getWindow(); - window.setPosition(mode.width / 2 - g.getWidth() / 2, mode.height / 2 - g.getHeight() / 2); - } - - @Override - public void sizeWindowToFit(int maxWidth, int maxHeight, int displayBorder, Graphics graphics) { - Graphics.DisplayMode mode = graphics.getDisplayMode(); - - int width = Math.min(mode.width - displayBorder * 2, maxWidth); - int height = Math.min(mode.height - displayBorder * 2, maxHeight); - - graphics.setWindowedMode(width, height); - - centerWindow(graphics); - } - - @Override - public void iconified(boolean isIconified) { - - } - - @Override - public void focusLost() { - - } - - @Override - public void focusGained() { - - } - - @Override - public boolean closeRequested() { - if (closeListener != null) { - return closeListener.closed(); - } else { - return true; - } - } - @Override - public void addFilesDroppedListener(FilesDroppedListener filesDroppedListener) { - filesDroppedListeners.add(filesDroppedListener); - } - - @Override - public void removeFilesDroppedListener(FilesDroppedListener filesDroppedListener) { - filesDroppedListeners.removeValue(filesDroppedListener, false); - } - - @Override - public void filesDropped(String[] files) { - Array fileHandles = new Array<>(); - for (String file : files) { - FileHandle fileHandle = new FileHandle(file); - fileHandles.add(fileHandle); - } - - for (FilesDroppedListener listener : filesDroppedListeners) { - listener.filesDropped(fileHandles); - } - } - - @Override - public void setCloseListener(CloseListener closeListener) { - this.closeListener = closeListener; - } - - @Override - public void attachLogListener() { - ((Lwjgl3Application) Gdx.app).setApplicationLogger(new TextFileApplicationLogger()); - } - - @Override - public void maximized(boolean arg0) { - } - - @Override - public void refreshRequested() { - } - - @Override - public List openMultipleDialog(String title, String defaultPath, - String[] filterPatterns, String filterDescription) { - String result = null; - - //fix file path characters - if (Utils.isWindows()) { - defaultPath = defaultPath.replace("/", "\\"); - } else { - defaultPath = defaultPath.replace("\\", "/"); - } - if (filterPatterns != null && filterPatterns.length > 0) { - try (MemoryStack stack = stackPush()) { - PointerBuffer pointerBuffer = stack.mallocPointer(filterPatterns.length); - - for (String filterPattern : filterPatterns) { - pointerBuffer.put(stack.UTF8(filterPattern)); - } - - pointerBuffer.flip(); - result = org.lwjgl.util.tinyfd.TinyFileDialogs.tinyfd_openFileDialog(title, defaultPath, pointerBuffer, filterDescription, true); - } - } else { - result = org.lwjgl.util.tinyfd.TinyFileDialogs.tinyfd_openFileDialog(title, defaultPath, null, filterDescription, true); - } - - if (result != null) { - String[] paths = result.split("\\|"); - ArrayList returnValue = new ArrayList<>(); - for (String path : paths) { - returnValue.add(new File(path)); - } - return returnValue; - } else { - return null; - } - } - - @Override - public File openDialog(String title, String defaultPath, - String[] filterPatterns, String filterDescription) { - String result = null; - - //fix file path characters - if (Utils.isWindows()) { - defaultPath = defaultPath.replace("/", "\\"); - } else { - defaultPath = defaultPath.replace("\\", "/"); - } - - if (filterPatterns != null && filterPatterns.length > 0) { - try (MemoryStack stack = stackPush()) { - PointerBuffer pointerBuffer = stack.mallocPointer(filterPatterns.length); - - for (String filterPattern : filterPatterns) { - pointerBuffer.put(stack.UTF8(filterPattern)); - } - - pointerBuffer.flip(); - result = org.lwjgl.util.tinyfd.TinyFileDialogs.tinyfd_openFileDialog(title, defaultPath, pointerBuffer, filterDescription, false); - } - } else { - result = org.lwjgl.util.tinyfd.TinyFileDialogs.tinyfd_openFileDialog(title, defaultPath, null, filterDescription, false); - } - - if (result != null) { - return new File(result); - } else { - return null; - } - } - - @Override - public File saveDialog(String title, String defaultPath, - String[] filterPatterns, String filterDescription) { - String result = null; - - //fix file path characters - if (Utils.isWindows()) { - defaultPath = defaultPath.replace("/", "\\"); - } else { - defaultPath = defaultPath.replace("\\", "/"); - } - - if (filterPatterns != null && filterPatterns.length > 0) { - try (MemoryStack stack = stackPush()) { - PointerBuffer pointerBuffer = null; - pointerBuffer = stack.mallocPointer(filterPatterns.length); - - for (String filterPattern : filterPatterns) { - pointerBuffer.put(stack.UTF8(filterPattern)); - } - - pointerBuffer.flip(); - result = org.lwjgl.util.tinyfd.TinyFileDialogs.tinyfd_saveFileDialog(title, defaultPath, pointerBuffer, filterDescription); - } - } else { - result = org.lwjgl.util.tinyfd.TinyFileDialogs.tinyfd_saveFileDialog(title, defaultPath, null, filterDescription); - } - - if (result != null) { - return new File(result); - } else { - return null; - } - } - @Override public void closeSplashScreen() { SplashScreen splash = SplashScreen.getSplashScreen(); @@ -330,71 +56,4 @@ public void closeSplashScreen() { splash.close(); } } - - @Override - public char getKeyName(int keyCode) { - int glfwKeyCode = Lwjgl3Input.getGlfwKeyCode(keyCode); - try { - String output = org.lwjgl.glfw.GLFW.glfwGetKeyName(glfwKeyCode, 0); - return (output == null) ? ' ' : output.toLowerCase().charAt(0); - } catch (Exception e) { - return ' '; - } - } - - @Override - public void writeFont(FreeTypeFontGenerator.FreeTypeBitmapFontData data, Array pages, FileHandle target) { - var pngTarget = target.sibling(target.nameWithoutExtension() + ".png"); - - BitmapFontWriter.FontInfo info = new BitmapFontWriter.FontInfo(); - data.capHeight--; - info.face = target.nameWithoutExtension(); - info.padding = new BitmapFontWriter.Padding(1, 1, 1, 1); - - BitmapFontWriter.writePixmaps(pages, target.parent(), target.nameWithoutExtension()); - - var pixmap = new Pixmap(pngTarget); - var color = new Color(); - var newHeight = pixmap.getHeight(); - var foundOpaquePixel = false; - for (var y = pixmap.getHeight() - 1; y >= 0 && !foundOpaquePixel; y--) { - for (var x = 0; x < pixmap.getWidth(); x++) { - color.set(pixmap.getPixel(x, y)); - if (color.a > 0) { - //add padding to new height - newHeight = y + 2; - foundOpaquePixel = true; - break; - } - } - } - - foundOpaquePixel = false; - var newWidth = pixmap.getWidth(); - for (var x = pixmap.getWidth() - 1; x >= 0 && !foundOpaquePixel; x--) { - for (var y = 0; y < pixmap.getHeight(); y++) { - color.set(pixmap.getPixel(x, y)); - if (color.a > 0) { - //add padding to new height - newWidth = x + 2; - foundOpaquePixel = true; - break; - } - } - } - - var fixedPixmap = new Pixmap(newWidth, newHeight, Pixmap.Format.RGBA8888); - fixedPixmap.setBlending(Pixmap.Blending.None); - fixedPixmap.drawPixmap(pixmap, 0, 0); - PixmapIO.writePNG(pngTarget, fixedPixmap); - - BitmapFontWriter.writeFont(data, new String[]{target.nameWithoutExtension() + ".png"}, target, info, newWidth, newHeight); - pixmap.dispose(); - fixedPixmap.dispose(); - } - - @Override - public void created(Lwjgl3Window lw) { - - } } diff --git a/desktop/src/com/ray3k/skincomposer/desktop/Launcher.java b/desktop/src/com/ray3k/skincomposer/desktop/Launcher.java new file mode 100644 index 00000000..daeb3077 --- /dev/null +++ b/desktop/src/com/ray3k/skincomposer/desktop/Launcher.java @@ -0,0 +1,385 @@ +/* + * The MIT License + * + * Copyright 2019 Raymond Buckley. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.ray3k.skincomposer.desktop; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.Graphics; +import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application; +import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration; +import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Graphics; +import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Input; +import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Window; +import com.badlogic.gdx.backends.lwjgl3.Lwjgl3WindowListener; +import com.badlogic.gdx.files.FileHandle; +import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.Pixmap; +import com.badlogic.gdx.graphics.PixmapIO; +import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.graphics.g2d.PixmapPacker; +import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator; +import com.badlogic.gdx.tools.bmfont.BitmapFontWriter; +import com.badlogic.gdx.tools.texturepacker.TexturePacker; +import com.badlogic.gdx.utils.Array; +import com.badlogic.gdx.utils.Json; +import com.ray3k.skincomposer.CloseListener; +import com.ray3k.skincomposer.DesktopWorker; +import com.ray3k.skincomposer.FilesDroppedListener; +import com.ray3k.skincomposer.Main; +import com.ray3k.skincomposer.TextFileApplicationLogger; +import com.ray3k.skincomposer.utils.Utils; +import java.io.File; +import java.io.FileWriter; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; +import javax.swing.JOptionPane; +import org.lwjgl.PointerBuffer; +import org.lwjgl.system.MemoryStack; +import static org.lwjgl.system.MemoryStack.stackPush; + +/** + * + * @author Raymond + */ +public class Launcher implements DesktopWorker, Lwjgl3WindowListener { + private Array filesDroppedListeners; + private CloseListener closeListener; + + public Launcher() { + filesDroppedListeners = new Array<>(); + } + + @Override + public void texturePack(Array handles, FileHandle localFile, FileHandle targetFile, FileHandle settingsFile) { + Json json = new Json(); + TexturePacker.Settings settings = json.fromJson(TexturePacker.Settings.class, settingsFile); + + TexturePacker p = new TexturePacker(settings); + for (FileHandle handle : handles) { + if (handle.exists()) { + p.addImage(handle.file()); + } else { + if (localFile != null) { + FileHandle localHandle = localFile.sibling(localFile.nameWithoutExtension() + "_data/" + handle.name()); + if (localHandle.exists()) { + p.addImage(localHandle.file()); + } else { + Gdx.app.error(getClass().getName(), "File does not exist error while creating texture atlas: " + handle.path()); + } + } else { + Gdx.app.error(getClass().getName(), "File does not exist error while creating texture atlas: " + handle.path()); + } + } + } + p.pack(targetFile.parent().file(), targetFile.nameWithoutExtension()); + } + + @Override + public void packFontImages(Array files, FileHandle saveFile) { + TexturePacker.Settings settings = new TexturePacker.Settings(); + settings.pot = false; + settings.duplicatePadding = true; + settings.filterMin = Texture.TextureFilter.Linear; + settings.filterMag = Texture.TextureFilter.Linear; + settings.ignoreBlankImages = false; + settings.useIndexes = false; + settings.limitMemory = false; + settings.maxWidth = 2048; + settings.maxHeight = 2048; + settings.flattenPaths = true; + settings.silent = true; + TexturePacker texturePacker = new TexturePacker(settings); + + for (FileHandle file : files) { + if (file.exists()) { + texturePacker.addImage(file.file()); + } + } + + texturePacker.pack(saveFile.parent().file(), saveFile.nameWithoutExtension()); + } + + @Override + public void centerWindow(Graphics graphics) { + Lwjgl3Graphics g = (Lwjgl3Graphics) graphics; + Graphics.DisplayMode mode = g.getDisplayMode(); + Lwjgl3Window window = g.getWindow(); + window.setPosition(mode.width / 2 - g.getWidth() / 2, mode.height / 2 - g.getHeight() / 2); + } + + @Override + public void sizeWindowToFit(int maxWidth, int maxHeight, int displayBorder, Graphics graphics) { + Graphics.DisplayMode mode = graphics.getDisplayMode(); + + int width = Math.min(mode.width - displayBorder * 2, maxWidth); + int height = Math.min(mode.height - displayBorder * 2, maxHeight); + + graphics.setWindowedMode(width, height); + + centerWindow(graphics); + } + + @Override + public void iconified(boolean isIconified) { + + } + + @Override + public void focusLost() { + + } + + @Override + public void focusGained() { + + } + + @Override + public boolean closeRequested() { + if (closeListener != null) { + return closeListener.closed(); + } else { + return true; + } + } + + @Override + public void addFilesDroppedListener(FilesDroppedListener filesDroppedListener) { + filesDroppedListeners.add(filesDroppedListener); + } + + @Override + public void removeFilesDroppedListener(FilesDroppedListener filesDroppedListener) { + filesDroppedListeners.removeValue(filesDroppedListener, false); + } + + @Override + public void filesDropped(String[] files) { + Array fileHandles = new Array<>(); + for (String file : files) { + FileHandle fileHandle = new FileHandle(file); + fileHandles.add(fileHandle); + } + + for (FilesDroppedListener listener : filesDroppedListeners) { + listener.filesDropped(fileHandles); + } + } + + @Override + public void setCloseListener(CloseListener closeListener) { + this.closeListener = closeListener; + } + + @Override + public void attachLogListener() { + ((Lwjgl3Application) Gdx.app).setApplicationLogger(new TextFileApplicationLogger()); + } + + @Override + public void maximized(boolean arg0) { + } + + @Override + public void refreshRequested() { + } + + @Override + public List openMultipleDialog(String title, String defaultPath, + String[] filterPatterns, String filterDescription) { + String result = null; + + //fix file path characters + if (Utils.isWindows()) { + defaultPath = defaultPath.replace("/", "\\"); + } else { + defaultPath = defaultPath.replace("\\", "/"); + } + if (filterPatterns != null && filterPatterns.length > 0) { + try (MemoryStack stack = stackPush()) { + PointerBuffer pointerBuffer = stack.mallocPointer(filterPatterns.length); + + for (String filterPattern : filterPatterns) { + pointerBuffer.put(stack.UTF8(filterPattern)); + } + + pointerBuffer.flip(); + result = org.lwjgl.util.tinyfd.TinyFileDialogs.tinyfd_openFileDialog(title, defaultPath, pointerBuffer, filterDescription, true); + } + } else { + result = org.lwjgl.util.tinyfd.TinyFileDialogs.tinyfd_openFileDialog(title, defaultPath, null, filterDescription, true); + } + + if (result != null) { + String[] paths = result.split("\\|"); + ArrayList returnValue = new ArrayList<>(); + for (String path : paths) { + returnValue.add(new File(path)); + } + return returnValue; + } else { + return null; + } + } + + @Override + public File openDialog(String title, String defaultPath, + String[] filterPatterns, String filterDescription) { + String result = null; + + //fix file path characters + if (Utils.isWindows()) { + defaultPath = defaultPath.replace("/", "\\"); + } else { + defaultPath = defaultPath.replace("\\", "/"); + } + + if (filterPatterns != null && filterPatterns.length > 0) { + try (MemoryStack stack = stackPush()) { + PointerBuffer pointerBuffer = stack.mallocPointer(filterPatterns.length); + + for (String filterPattern : filterPatterns) { + pointerBuffer.put(stack.UTF8(filterPattern)); + } + + pointerBuffer.flip(); + result = org.lwjgl.util.tinyfd.TinyFileDialogs.tinyfd_openFileDialog(title, defaultPath, pointerBuffer, filterDescription, false); + } + } else { + result = org.lwjgl.util.tinyfd.TinyFileDialogs.tinyfd_openFileDialog(title, defaultPath, null, filterDescription, false); + } + + if (result != null) { + return new File(result); + } else { + return null; + } + } + + @Override + public File saveDialog(String title, String defaultPath, + String[] filterPatterns, String filterDescription) { + String result = null; + + //fix file path characters + if (Utils.isWindows()) { + defaultPath = defaultPath.replace("/", "\\"); + } else { + defaultPath = defaultPath.replace("\\", "/"); + } + + if (filterPatterns != null && filterPatterns.length > 0) { + try (MemoryStack stack = stackPush()) { + PointerBuffer pointerBuffer = null; + pointerBuffer = stack.mallocPointer(filterPatterns.length); + + for (String filterPattern : filterPatterns) { + pointerBuffer.put(stack.UTF8(filterPattern)); + } + + pointerBuffer.flip(); + result = org.lwjgl.util.tinyfd.TinyFileDialogs.tinyfd_saveFileDialog(title, defaultPath, pointerBuffer, filterDescription); + } + } else { + result = org.lwjgl.util.tinyfd.TinyFileDialogs.tinyfd_saveFileDialog(title, defaultPath, null, filterDescription); + } + + if (result != null) { + return new File(result); + } else { + return null; + } + } + + @Override + public void closeSplashScreen() { + } + + @Override + public char getKeyName(int keyCode) { + int glfwKeyCode = Lwjgl3Input.getGlfwKeyCode(keyCode); + try { + String output = org.lwjgl.glfw.GLFW.glfwGetKeyName(glfwKeyCode, 0); + return (output == null) ? ' ' : output.toLowerCase().charAt(0); + } catch (Exception e) { + return ' '; + } + } + + @Override + public void writeFont(FreeTypeFontGenerator.FreeTypeBitmapFontData data, Array pages, FileHandle target) { + FileHandle pngTarget = target.sibling(target.nameWithoutExtension() + ".png"); + + BitmapFontWriter.FontInfo info = new BitmapFontWriter.FontInfo(); + data.capHeight--; + info.face = target.nameWithoutExtension(); + info.padding = new BitmapFontWriter.Padding(1, 1, 1, 1); + + BitmapFontWriter.writePixmaps(pages, target.parent(), target.nameWithoutExtension()); + + Pixmap pixmap = new Pixmap(pngTarget); + Color color = new Color(); + int newHeight = pixmap.getHeight(); + boolean foundOpaquePixel = false; + for (int y = pixmap.getHeight() - 1; y >= 0 && !foundOpaquePixel; y--) { + for (int x = 0; x < pixmap.getWidth(); x++) { + color.set(pixmap.getPixel(x, y)); + if (color.a > 0) { + //add padding to new height + newHeight = y + 2; + foundOpaquePixel = true; + break; + } + } + } + + foundOpaquePixel = false; + int newWidth = pixmap.getWidth(); + for (int x = pixmap.getWidth() - 1; x >= 0 && !foundOpaquePixel; x--) { + for (int y = 0; y < pixmap.getHeight(); y++) { + color.set(pixmap.getPixel(x, y)); + if (color.a > 0) { + //add padding to new height + newWidth = x + 2; + foundOpaquePixel = true; + break; + } + } + } + + Pixmap fixedPixmap = new Pixmap(newWidth, newHeight, Pixmap.Format.RGBA8888); + fixedPixmap.setBlending(Pixmap.Blending.None); + fixedPixmap.drawPixmap(pixmap, 0, 0); + PixmapIO.writePNG(pngTarget, fixedPixmap); + + BitmapFontWriter.writeFont(data, new String[]{target.nameWithoutExtension() + ".png"}, target, info, newWidth, newHeight); + pixmap.dispose(); + fixedPixmap.dispose(); + } + + @Override + public void created(Lwjgl3Window lw) { + + } +} diff --git a/desktop/src/com/ray3k/skincomposer/desktop/MacLauncher.java b/desktop/src/com/ray3k/skincomposer/desktop/MacLauncher.java new file mode 100644 index 00000000..0f552886 --- /dev/null +++ b/desktop/src/com/ray3k/skincomposer/desktop/MacLauncher.java @@ -0,0 +1,50 @@ +package com.ray3k.skincomposer.desktop; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application; +import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration; +import com.ray3k.skincomposer.Main; +import com.ray3k.skincomposer.utils.Utils; +import java.io.FileWriter; +import java.io.PrintWriter; +import javax.swing.JOptionPane; + +public class MacLauncher extends Launcher { + public static void main(String[] args) { + Lwjgl3ApplicationConfiguration config = new Lwjgl3ApplicationConfiguration(); + config.setResizable(true); + config.useVsync(true); + config.setWindowedMode(800, 800); + MacLauncher desktopLauncher = new MacLauncher(); + config.setWindowListener(desktopLauncher); + config.setTitle("Skin Composer - New Project*"); + config.setWindowSizeLimits(675, 400, -1, -1); + config.setWindowIcon("logo-16.png", "logo-32.png", "logo-48.png", "logo.png"); + Main main = new Main(args); + main.setDesktopWorker(desktopLauncher); + if (!Utils.isWindows()) { + desktopLauncher.closeSplashScreen(); + } + + try { + new Lwjgl3Application(main, config); + } catch (Exception e) { + e.printStackTrace(); + + FileWriter fw = null; + try { + fw = new FileWriter(Gdx.files.external(".skincomposer/temp/java-stacktrace.txt").file(), true); + PrintWriter pw = new PrintWriter(fw); + e.printStackTrace(pw); + pw.close(); + fw.close(); + int choice = JOptionPane.showConfirmDialog(null, "Exception occurred. See error log?", "Skin Composer Exception!", JOptionPane.YES_NO_OPTION); + if (choice == 0) { + Utils.openFileExplorer(Gdx.files.external(".skincomposer/temp/java-stacktrace.txt")); + } + } catch (Exception ex) { + + } + } + } +} diff --git a/docs/images/logo.png b/docs/images/logo.png index 5aedbc44..baba40ff 100644 Binary files a/docs/images/logo.png and b/docs/images/logo.png differ diff --git a/version b/version index a5c750fe..368f89ce 100644 --- a/version +++ b/version @@ -1 +1 @@ -27 \ No newline at end of file +28 \ No newline at end of file