diff --git a/assets/plugins/plugin-skin-composer-0.1.5.jar b/assets/plugins/plugin-skin-composer-0.1.5.jar index db8ca394..720a40e7 100644 Binary files a/assets/plugins/plugin-skin-composer-0.1.5.jar and b/assets/plugins/plugin-skin-composer-0.1.5.jar differ diff --git a/build.gradle b/build.gradle index 39f9d2aa..7a7c2b10 100644 --- a/build.gradle +++ b/build.gradle @@ -140,7 +140,7 @@ dependencies { implementation "com.github.lyze237:gdx-TinyVG:$gdxTinyVGVersion" implementation "com.mortennobel:java-image-scaling:0.8.6" implementation "org.apache.commons:commons-lang3:3.12.0" - implementation "commons-io:commons-io:2.7" + implementation "commons-io:commons-io:2.16.1" implementation 'net.mountainblade:modular:1.0' testImplementation group: 'junit', name: 'junit', version: '4.12' diff --git a/hyperlap2d-common-api b/hyperlap2d-common-api index 1c02946a..00ec5749 160000 --- a/hyperlap2d-common-api +++ b/hyperlap2d-common-api @@ -1 +1 @@ -Subproject commit 1c02946af957d653ed07604ccd9d9388f70e74b5 +Subproject commit 00ec5749797633fea98216bdb0a07c62072ebb7a diff --git a/hyperlap2d-runtime-libgdx b/hyperlap2d-runtime-libgdx index e0312067..85bcd25f 160000 --- a/hyperlap2d-runtime-libgdx +++ b/hyperlap2d-runtime-libgdx @@ -1 +1 @@ -Subproject commit e0312067e90acf99fb8cbae519aabd0f86fd3234 +Subproject commit 85bcd25fc4ba8255587fd62fe6654b56261e49c3 diff --git a/plugin-skin-composer/build.gradle b/plugin-skin-composer/build.gradle index 1f90b76a..5b3753dd 100644 --- a/plugin-skin-composer/build.gradle +++ b/plugin-skin-composer/build.gradle @@ -5,8 +5,10 @@ plugins { group 'games.rednblack' repositories { + mavenLocal() mavenCentral() maven { url "https://oss.sonatype.org/content/repositories/snapshots/" } + maven { url 'https://jitpack.io' } } dependencies { @@ -21,7 +23,7 @@ dependencies { implementation project(":hyperlap2d-common-api") implementation project(":hyperlap2d-runtime-libgdx") - implementation "org.apache.commons:commons-io:1.3.2" + implementation "commons-io:commons-io:2.16.1" testImplementation group: 'junit', name: 'junit', version: '4.12' } diff --git a/src/main/java/games/rednblack/editor/proxy/ProjectManager.java b/src/main/java/games/rednblack/editor/proxy/ProjectManager.java index 5fc1a82f..0b98cb5d 100755 --- a/src/main/java/games/rednblack/editor/proxy/ProjectManager.java +++ b/src/main/java/games/rednblack/editor/proxy/ProjectManager.java @@ -44,14 +44,9 @@ import games.rednblack.h2d.common.ProgressHandler; import games.rednblack.h2d.common.vo.ProjectVO; import games.rednblack.h2d.common.vo.SceneConfigVO; import games.rednblack.h2d.common.vo.TexturePackerVO; -import games.rednblack.puremvc.Facade; import games.rednblack.puremvc.Proxy; import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; -import org.apache.commons.io.monitor.FileAlterationListener; -import org.apache.commons.io.monitor.FileAlterationListenerAdaptor; -import org.apache.commons.io.monitor.FileAlterationMonitor; -import org.apache.commons.io.monitor.FileAlterationObserver; import org.lwjgl.util.tinyfd.TinyFileDialogs; import javax.imageio.ImageIO; @@ -59,10 +54,14 @@ import java.awt.image.BufferedImage; import java.io.File; import java.io.FileFilter; import java.io.IOException; +import java.nio.file.*; +import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; public class ProjectManager extends Proxy { private static final String TAG = ProjectManager.class.getCanonicalName(); @@ -93,7 +92,7 @@ public class ProjectManager extends Proxy { private ProjectExportSettings projectExportSettings; private LivePreviewSettings livePreviewSettings; - private FileAlterationMonitor fileWatcherMonitor; + private Thread fileWatcherThread; @Override public void onRegister() { @@ -241,43 +240,84 @@ public class ProjectManager extends Proxy { } } - private void addFileWatcher(String projectPath) throws Exception { + private void addFileWatcher(String projectPath) { stopFileWatcher(); - fileWatcherMonitor = new FileAlterationMonitor(2000); + if (fileWatcherThread != null) return; - FileAlterationObserver observer = new FileAlterationObserver(projectPath); - FileAlterationListener listener = new FileAlterationListenerAdaptor() { + Path directory = Paths.get(projectPath); + + fileWatcherThread = new Thread(new Runnable() { @Override - public void onFileCreate(File file) { - Gdx.app.postRunnable(() -> facade.sendNotification(MsgAPI.PROJECT_FILE_CREATED, file)); - } + public void run() { + try (WatchService watchService = FileSystems.getDefault().newWatchService()) { + registerAll(directory, watchService); - @Override - public void onFileDelete(File file) { - Gdx.app.postRunnable(() -> facade.sendNotification(MsgAPI.PROJECT_FILE_DELETED, file)); - } + while (true) { + WatchKey key; + try { + // Poll for file system events + key = watchService.poll(2, TimeUnit.SECONDS); + } catch (InterruptedException e) { + return; + } - @Override - public void onFileChange(File file) { - Gdx.app.postRunnable(() -> facade.sendNotification(MsgAPI.PROJECT_FILE_MODIFIED, file)); - } - }; - observer.addListener(listener); + if (key == null) { + // No events within the timeout period + continue; + } - fileWatcherMonitor.addObserver(observer); - fileWatcherMonitor.start(); + List> events = key.pollEvents(); + + for (int i = 0; i < events.size(); i++) { + WatchEvent event = events.get(i); + WatchEvent.Kind kind = event.kind(); + Path fileName = (Path) event.context(); + Path filePath = ((Path) key.watchable()).resolve(fileName); + File file = filePath.toFile(); + + if (kind == StandardWatchEventKinds.ENTRY_CREATE) { + Gdx.app.postRunnable(() -> facade.sendNotification(MsgAPI.PROJECT_FILE_CREATED, file)); + } else if (kind == StandardWatchEventKinds.ENTRY_DELETE) { + Gdx.app.postRunnable(() -> facade.sendNotification(MsgAPI.PROJECT_FILE_DELETED, file)); + } else if (kind == StandardWatchEventKinds.ENTRY_MODIFY) { + Gdx.app.postRunnable(() -> facade.sendNotification(MsgAPI.PROJECT_FILE_MODIFIED, file)); + } + } + + // Reset the key + boolean valid = key.reset(); + if (!valid) { + break; // Exit the loop if the key is no longer valid + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + }, "FileWatcherThread"); + + fileWatcherThread.setDaemon(true); + fileWatcherThread.start(); } public void stopFileWatcher() { - if (fileWatcherMonitor != null) { - try { - fileWatcherMonitor.stop(); - } catch (Exception e) { - e.printStackTrace(); + if (fileWatcherThread == null) return; + fileWatcherThread.interrupt(); + fileWatcherThread = null; + } + + private void registerAll(final Path start, final WatchService watchService) throws Exception { + // Register the directory and its subdirectories recursively + Files.walkFileTree(start, new SimpleFileVisitor() { + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + dir.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, + StandardWatchEventKinds.ENTRY_DELETE, + StandardWatchEventKinds.ENTRY_MODIFY); + return FileVisitResult.CONTINUE; } - fileWatcherMonitor = null; - } + }); } private void goThroughVersionMigrationProtocol(String projectPath, ProjectVO projectVo) { diff --git a/src/main/java/games/rednblack/editor/view/ui/RulersUI.java b/src/main/java/games/rednblack/editor/view/ui/RulersUI.java index a4b33245..458e3eb6 100644 --- a/src/main/java/games/rednblack/editor/view/ui/RulersUI.java +++ b/src/main/java/games/rednblack/editor/view/ui/RulersUI.java @@ -10,6 +10,7 @@ import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.InputEvent; import com.badlogic.gdx.scenes.scene2d.utils.ClickListener; import com.badlogic.gdx.utils.Array; +import com.badlogic.gdx.utils.IntMap; import com.badlogic.gdx.utils.Pools; import com.kotcrab.vis.ui.widget.VisLabel; import games.rednblack.editor.utils.Guide; @@ -73,7 +74,7 @@ public class RulersUI extends Actor { private final Circle tmpCircle = new Circle(); private final Color tmpColor = new Color(); - private final HashMap labelTextCache = new HashMap<>(); + private final IntMap labelTextCache = new IntMap<>(); public RulersUI() { horizontalRect = new Rectangle(); @@ -324,7 +325,8 @@ public class RulersUI extends Actor { VisLabel label = Pools.obtain(VisLabel.class); label.setColor(TEXT_COLOR); int textNumber = (int) Math.abs(worldStartPointCpy.y + iterator * gridMeasuringSize); - labelTextCache.putIfAbsent(textNumber, ""); + if (labelTextCache.get(textNumber) == null) + labelTextCache.put(textNumber, ""); String lblText = labelTextCache.get(textNumber); if (lblText.equals("")) { lblText = verticalize(textNumber + ""); diff --git a/src/main/java/games/rednblack/editor/view/ui/dialog/CodeEditorDialogMediator.java b/src/main/java/games/rednblack/editor/view/ui/dialog/CodeEditorDialogMediator.java index 9e247087..9671e818 100644 --- a/src/main/java/games/rednblack/editor/view/ui/dialog/CodeEditorDialogMediator.java +++ b/src/main/java/games/rednblack/editor/view/ui/dialog/CodeEditorDialogMediator.java @@ -26,7 +26,7 @@ public class CodeEditorDialogMediator extends Mediator { @Override public void listNotificationInterests(Interests interests) { interests.add(MsgAPI.OPEN_CODE_EDITOR, - MsgAPI.PROJECT_FILE_MODIFIED); + MsgAPI.PROJECT_FILE_MODIFIED, MsgAPI.PROJECT_FILE_CREATED); } @Override @@ -47,6 +47,7 @@ public class CodeEditorDialogMediator extends Mediator { readObservedFile(); } break; + case MsgAPI.PROJECT_FILE_CREATED: case MsgAPI.PROJECT_FILE_MODIFIED: if (!viewComponent.hasParent()) break;