diff --git a/.idea/encodings.xml b/.idea/encodings.xml index eccafc183acbddc53097dae4f91cf41f05d2b421..c1500a33cb5596556ab38560f449acbae8fbe473 100644 --- a/.idea/encodings.xml +++ b/.idea/encodings.xml @@ -16,6 +16,7 @@ <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginAwake" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginAwake/src/main/java" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginAwake/src/main/resources" charset="UTF-8" /> + <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginEqualizer" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginEqualizer/src/main/java" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginEqualizer/src/main/resources" charset="UTF-8" /> diff --git a/.idea/misc.xml b/.idea/misc.xml index 8f099dcf1c0f4d54403502bdf9db55780d811921..ca57f98e5686aa62d0af34128539b5e49bdf8acc 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -33,7 +33,6 @@ <option name="ignoredFiles"> <set> <option value="$PROJECT_DIR$/PlayWall/PlayWallComponents/pom.xml" /> - <option value="$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginContentPlayer/pom.xml" /> </set> </option> </component> diff --git a/.idea/modules.xml b/.idea/modules.xml index 71a1574f2f6df494b2028c78bb51cb89fc3ba39e..ae20783ca82797f27ca5b049467c233d031158e8 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -7,6 +7,7 @@ <module fileurl="file://$PROJECT_DIR$/PlayWallCore/PlayWallCore.iml" filepath="$PROJECT_DIR$/PlayWallCore/PlayWallCore.iml" /> <module fileurl="file://$PROJECT_DIR$/PlayWallDesktop.iml" filepath="$PROJECT_DIR$/PlayWallDesktop.iml" /> <module fileurl="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginAwake/PlayWallPluginAwake.iml" filepath="$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginAwake/PlayWallPluginAwake.iml" /> + <module fileurl="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginContentPlayer/PlayWallPluginContentPlayer.iml" filepath="$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginContentPlayer/PlayWallPluginContentPlayer.iml" /> <module fileurl="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginEqualizer/PlayWallPluginEqualizer.iml" filepath="$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginEqualizer/PlayWallPluginEqualizer.iml" /> <module fileurl="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginLaunchpad/PlayWallPluginLaunchpad.iml" filepath="$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginLaunchpad/PlayWallPluginLaunchpad.iml" /> <module fileurl="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginMedia/PlayWallPluginMedia.iml" filepath="$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginMedia/PlayWallPluginMedia.iml" /> diff --git a/PlayWall/src/main/java/de/tobias/playpad/PlayPadMain.java b/PlayWall/src/main/java/de/tobias/playpad/PlayPadMain.java index 4432218dad6cd31259783a0c8dc80af4e9fa473a..79b2b7b6aae33c837fff88e2546954a85186fb52 100644 --- a/PlayWall/src/main/java/de/tobias/playpad/PlayPadMain.java +++ b/PlayWall/src/main/java/de/tobias/playpad/PlayPadMain.java @@ -18,6 +18,7 @@ import de.tobias.playpad.profile.ref.ProfileReferenceManager; import de.tobias.playpad.project.Project; import de.tobias.playpad.project.ref.ProjectReferenceManager; import de.tobias.playpad.update.VersionUpdater; +import de.tobias.playpad.util.ListSerializer; import de.tobias.playpad.util.UUIDSerializer; import de.tobias.playpad.viewcontroller.SplashScreenViewController; import io.github.openunirest.http.Unirest; @@ -36,6 +37,7 @@ import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import java.security.cert.X509Certificate; +import java.util.ArrayList; import java.util.UUID; /* @@ -65,6 +67,7 @@ public class PlayPadMain extends Application { // Register UserDefaults Serializer UserDefaults.registerLoader(new UUIDSerializer(), UUID.class); + UserDefaults.registerLoader(new ListSerializer(), ArrayList.class); ApplicationUtils.addAppListener(PlayPadMain::applicationWillStart); App app = ApplicationUtils.registerMainApplication(PlayPadMain.class); diff --git a/PlayWall/src/main/java/de/tobias/playpad/Strings.java b/PlayWall/src/main/java/de/tobias/playpad/Strings.java index b28adcf21884f13366e14acc1b2f28b5a7ad0b00..e71f1944b156d2853018669b92e0ba6366fa9595 100644 --- a/PlayWall/src/main/java/de/tobias/playpad/Strings.java +++ b/PlayWall/src/main/java/de/tobias/playpad/Strings.java @@ -173,6 +173,7 @@ public class Strings { // UI - Window - PadSettings public static final String UI_WINDOW_PAD_SETTINGS_GENERAL_TITLE = "UI.Window.PadSettings.General.Title"; + public static final String UI_WINDOW_PAD_SETTINGS_PLAYLIST_TITLE = "UI.Window.PadSettings.Playlist.Title"; public static final String UI_WINDOW_PAD_SETTINGS_PLAYER_TITLE = "UI.Window.PadSettings.Player.Title"; public static final String UI_WINDOW_PAD_SETTINGS_LAYOUT_TITLE = "UI.Window.PadSettings.Layout.Title"; public static final String UI_WINDOW_PAD_SETTINGS_TRIGGER_TITLE = "UI.Window.PadSettings.Trigger.Title"; diff --git a/PlayWall/src/main/java/de/tobias/playpad/design/ModernDesignAnimator.java b/PlayWall/src/main/java/de/tobias/playpad/design/ModernDesignAnimator.java index d7ebadfc7f1e14ae7e77ffa5cad5ae4d618f71e5..ffd70e7b8b949a039aeb21718fdd335de4193f16 100644 --- a/PlayWall/src/main/java/de/tobias/playpad/design/ModernDesignAnimator.java +++ b/PlayWall/src/main/java/de/tobias/playpad/design/ModernDesignAnimator.java @@ -1,7 +1,7 @@ package de.tobias.playpad.design; import de.tobias.playpad.pad.view.IPadView; -import de.tobias.playpad.pad.viewcontroller.IPadViewController; +import de.tobias.playpad.pad.viewcontroller.AbstractPadViewController; import de.tobias.playpad.util.FadeableColor; import de.tobias.playpad.view.PseudoClasses; import javafx.animation.KeyFrame; @@ -23,7 +23,7 @@ public class ModernDesignAnimator { private static HashMap<Integer, Timeline> timelines = new HashMap<>(); - public static void animateFade(IPadViewController padViewController, FadeableColor startColor, FadeableColor endColor, Duration duration) { + public static void animateFade(AbstractPadViewController padViewController, FadeableColor startColor, FadeableColor endColor, Duration duration) { ObjectProperty<FadeableColor> backgroundColor = new SimpleObjectProperty<>(); Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(0), new KeyValue(backgroundColor, startColor)), @@ -32,7 +32,7 @@ public class ModernDesignAnimator { animate(padViewController, timeline, backgroundColor); } - public static void animateWarn(IPadViewController padViewController, FadeableColor startColor, FadeableColor endColor, Duration duration) { + public static void animateWarn(AbstractPadViewController padViewController, FadeableColor startColor, FadeableColor endColor, Duration duration) { ObjectProperty<FadeableColor> backgroundColor = new SimpleObjectProperty<>(); Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(0), new KeyValue(backgroundColor, startColor)), @@ -45,7 +45,7 @@ public class ModernDesignAnimator { animate(padViewController, timeline, backgroundColor); } - private static void animate(IPadViewController padViewController, Timeline timeline, ObjectProperty<FadeableColor> objectProperty) { + private static void animate(AbstractPadViewController padViewController, Timeline timeline, ObjectProperty<FadeableColor> objectProperty) { int index = padViewController.getPad().getPosition(); if (timelines.containsKey(index)) { @@ -68,7 +68,7 @@ public class ModernDesignAnimator { timelines.put(index, timeline); } - public static void stopAnimation(IPadViewController controller) { + public static void stopAnimation(AbstractPadViewController controller) { int index = controller.getPad().getPosition(); if (timelines.containsKey(index)) { @@ -76,7 +76,7 @@ public class ModernDesignAnimator { } } - public static void warnFlash(IPadViewController controller) { + public static void warnFlash(AbstractPadViewController controller) { final IPadView view = controller.getView(); try { while (!Thread.interrupted()) { diff --git a/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/listener/PadNewContentListener.java b/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/listener/PadNewContentListener.java index a2470391be71a7117f460051e78c274d269877e8..fedd12aea0879dbda04f8cc41b28c335c4807a8e 100644 --- a/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/listener/PadNewContentListener.java +++ b/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/listener/PadNewContentListener.java @@ -13,10 +13,12 @@ import de.tobias.playpad.settings.GlobalSettings; import javafx.event.ActionEvent; import javafx.scene.Node; import javafx.stage.FileChooser; +import javafx.stage.Window; import java.io.File; import java.nio.file.Path; -import java.util.Set; +import java.util.Collections; +import java.util.List; public class PadNewContentListener { @@ -26,17 +28,14 @@ public class PadNewContentListener { this.pad = pad; } - public void onNew(ActionEvent event, PadContentFactory.PadContentTypeChooser padContentTypeChooser) throws NoSuchComponentException { + public List<File> showMediaOpenFileChooser(ActionEvent event, String[] supportedFileTypes, boolean multiSelect) { GlobalSettings settings = PlayPadPlugin.getInstance().getGlobalSettings(); if (pad.getProject() != null && settings.isLiveMode() && settings.isLiveModeFile() && pad.getProject().getActivePlayers() > 0) { - return; + return Collections.emptyList(); } final FileChooser chooser = new FileChooser(); - PadContentRegistry registry = PlayPadPlugin.getRegistries().getPadContents(); - - // File Extension - final FileChooser.ExtensionFilter extensionFilter = new FileChooser.ExtensionFilter(Localization.getString(Strings.FILE_FILTER_MEDIA), registry.getSupportedFileTypes()); + final FileChooser.ExtensionFilter extensionFilter = new FileChooser.ExtensionFilter(Localization.getString(Strings.FILE_FILTER_MEDIA), supportedFileTypes); chooser.getExtensionFilters().add(extensionFilter); // Last Folder @@ -48,11 +47,28 @@ public class PadNewContentListener { } } - final File file = chooser.showOpenDialog(((Node) event.getTarget()).getScene().getWindow()); - if (file != null) { - Path path = file.toPath(); + final List<File> selectedFiles; + final Window window = ((Node) event.getTarget()).getScene().getWindow(); + if (multiSelect) { + selectedFiles = chooser.showOpenMultipleDialog(window); + } else { + selectedFiles = Collections.singletonList(chooser.showOpenDialog(window)); + } + + if (selectedFiles != null && !selectedFiles.isEmpty()) { + ApplicationUtils.getApplication().getUserDefaults().setData(DesktopPadViewController.OPEN_FOLDER, selectedFiles.get(0).getParent()); + } + return selectedFiles; + } + + public void onNew(ActionEvent event, PadContentFactory.PadContentTypeChooser padContentTypeChooser) throws NoSuchComponentException { + final PadContentRegistry registry = PlayPadPlugin.getRegistries().getPadContents(); + final List<File> files = showMediaOpenFileChooser(event, registry.getSupportedFileTypes(), false); - final Set<PadContentFactory> connects = registry.getPadContentConnectsForFile(file.toPath()); + if (files != null) { + final Path path = files.get(0).toPath(); + + final List<PadContentFactory> connects = registry.getPadContentConnectsForFile(path); if (!connects.isEmpty()) { if (connects.size() > 1) { // Multiple content types possible padContentTypeChooser.showOptions(connects, padContent -> @@ -66,8 +82,6 @@ public class PadNewContentListener { setNewPadContent(path, padContent); } } - - ApplicationUtils.getApplication().getUserDefaults().setData(DesktopPadViewController.OPEN_FOLDER, path.getParent().toString()); } } diff --git a/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/pad/DesktopPadDragListener.java b/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/pad/DesktopPadDragListener.java index 637686246100491b6b093d6fd525637741fcd489..fff46f264a0632184ab597a851d5eba90fcf7b47 100644 --- a/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/pad/DesktopPadDragListener.java +++ b/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/pad/DesktopPadDragListener.java @@ -7,7 +7,10 @@ import de.tobias.playpad.pad.Pad; import de.tobias.playpad.pad.PadStatus; import de.tobias.playpad.pad.content.PadContentFactory; import de.tobias.playpad.pad.content.PadContentRegistry; +import de.tobias.playpad.pad.content.Playlistable; +import de.tobias.playpad.pad.drag.ContentDragOption; import de.tobias.playpad.pad.drag.PadDragMode; +import de.tobias.playpad.pad.drag.PlaylistDragOption; import de.tobias.playpad.pad.view.IPadView; import de.tobias.playpad.profile.Profile; import de.tobias.playpad.project.Project; @@ -26,18 +29,21 @@ import javafx.scene.layout.Pane; import javafx.scene.paint.Color; import java.io.File; +import java.nio.file.Path; +import java.util.ArrayList; import java.util.Collection; -import java.util.Set; +import java.util.List; +import java.util.stream.Collectors; public class DesktopPadDragListener implements EventHandler<DragEvent> { - private static final String PADINDEX_DATATYPE = "de.tobias.playpad.padindex"; - private static final DataFormat dataFormat = new DataFormat(PADINDEX_DATATYPE); + private static final String PAD_INDEX_DATATYPE = "de.tobias.playpad.pad_index"; + private static final DataFormat dataFormat = new DataFormat(PAD_INDEX_DATATYPE); - private Pad currentPad; - private final Pane padView; // Node der PadView + private final Pad currentPad; + private final Pane padViewNode; // Node der PadView - private DesktopMainLayoutFactory connect; + private final DesktopMainLayoutFactory connect; private PadDragOptionView padHud; private FileDragOptionView fileHud; @@ -46,21 +52,21 @@ public class DesktopPadDragListener implements EventHandler<DragEvent> { this.currentPad = currentPad; this.connect = connect; - this.padView = view.getRootNode(); + this.padViewNode = view.getRootNode(); } public void addListener() { - this.padView.setOnDragOver(this::dragOver); - this.padView.setOnDragExited(event -> dragExited()); - this.padView.setOnDragDropped(this::dragDropped); - this.padView.setOnDragDetected(this::dragDetacted); + this.padViewNode.setOnDragOver(this::dragOver); + this.padViewNode.setOnDragExited(event -> dragExited()); + this.padViewNode.setOnDragDropped(this::dragDropped); + this.padViewNode.setOnDragDetected(this::dragDetected); } void removeListener() { - this.padView.setOnDragOver(null); - this.padView.setOnDragExited(null); - this.padView.setOnDragDropped(null); - this.padView.setOnDragDetected(null); + this.padViewNode.setOnDragOver(null); + this.padViewNode.setOnDragExited(null); + this.padViewNode.setOnDragDropped(null); + this.padViewNode.setOnDragDetected(null); } @Override @@ -75,52 +81,67 @@ public class DesktopPadDragListener implements EventHandler<DragEvent> { } private void dragOver(DragEvent event) { - if (Profile.currentProfile().getProfileSettings().isLocked()) { + if (Profile.currentProfile().getProfileSettings().isLocked() || checkLiveMode()) { return; } if (event.getGestureSource() != this && event.getDragboard().hasFiles()) { - File file = event.getDragboard().getFiles().get(0); - if (file.isFile()) { - // Check Live Mode - if (checkLiveMode()) { - return; - } + handleFileDropOver(event); + } else if (event.getDragboard().hasContent(dataFormat)) { + handlePadDragOver(event); + } + event.consume(); + } + + @SuppressWarnings("java:S1066") + private void handleFileDropOver(DragEvent event) { + final File file = event.getDragboard().getFiles().get(0); + if (file.isFile()) { - // Build In Filesupport - PadContentRegistry registry = PlayPadPlugin.getRegistries().getPadContents(); - Set<PadContentFactory> connects = registry.getPadContentConnectsForFile(file.toPath()); + final List<Path> paths = event.getDragboard().getFiles().stream().map(File::toPath).collect(Collectors.toList()); - if (!connects.isEmpty()) { - if (fileHud == null) { - fileHud = new FileDragOptionView(padView); - } - fileHud.showOptions(connects); + // built-in file support + final PadContentRegistry registry = PlayPadPlugin.getRegistries().getPadContents(); + final List<PadContentFactory> supportedContentTypes = registry.getPadContentConnectsForFiles(paths); - event.acceptTransferModes(TransferMode.LINK); - return; + if (currentPad.getContent() != null && currentPad.getContent().isPadLoading()) { + return; + } + + final List<ContentDragOption> contentDragOptions = new ArrayList<>(supportedContentTypes); + + if (currentPad.getContent() instanceof Playlistable) { + if (supportedContentTypes.stream().anyMatch(factory -> factory.getType().equals(currentPad.getContent().getType()))) { + contentDragOptions.add(new PlaylistDragOption()); } } - } - // Drag and Drop von Pads - if (event.getDragboard().hasContent(dataFormat)) { - PadIndex index = (PadIndex) event.getDragboard().getContent(dataFormat); - if (!currentPad.getPadIndex().equals(index)) { + if (!contentDragOptions.isEmpty()) { + if (fileHud == null) { + fileHud = new FileDragOptionView(padViewNode); + } + fileHud.showOptions(contentDragOptions); + + event.acceptTransferModes(TransferMode.LINK); + } + } + } - Collection<PadDragMode> connects = PlayPadPlugin.getRegistries().getDragModes().getComponents(); + private void handlePadDragOver(DragEvent event) { + PadIndex index = (PadIndex) event.getDragboard().getContent(dataFormat); + if (!currentPad.getPadIndex().equals(index)) { - if (!connects.isEmpty()) { - if (padHud == null) { - padHud = new PadDragOptionView(padView); - } - padHud.showDropOptions(connects); + Collection<PadDragMode> connects = PlayPadPlugin.getRegistries().getDragModes().getComponents(); - event.acceptTransferModes(TransferMode.MOVE); + if (!connects.isEmpty()) { + if (padHud == null) { + padHud = new PadDragOptionView(padViewNode); } + padHud.showDropOptions(connects); + + event.acceptTransferModes(TransferMode.MOVE); } } - event.consume(); } private void dragExited() { @@ -134,79 +155,83 @@ public class DesktopPadDragListener implements EventHandler<DragEvent> { // Drag Content ist los gelassen am Ziel private void dragDropped(DragEvent event) { - Project project = PlayPadPlugin.getInstance().getCurrentProject(); + final Project project = PlayPadPlugin.getInstance().getCurrentProject(); - Dragboard db = event.getDragboard(); + final Dragboard dragboard = event.getDragboard(); boolean success = false; // File Handling - if (db.hasFiles()) { - File file = db.getFiles().get(0); - - PadContentFactory connect = fileHud.getSelectedConnect(); - if (connect != null) { - // stop pad if playing - if(currentPad.getContent() != null && currentPad.getStatus().equals(PadStatus.PLAY)) { - currentPad.getContent().stop(); - currentPad.stop(); - } + if (dragboard.hasFiles()) { + success = handleFileDragDropped(dragboard); + } - if (currentPad.getContent() == null || !currentPad.getContent().getType().equals(connect.getType())) { - currentPad.setContentType(connect.getType()); - } + // Pad DnD + if (dragboard.hasContent(dataFormat)) { + success = handlePadDragDropped(project, dragboard); + } + // Event Completion + event.setDropCompleted(success); + event.consume(); + } - if (currentPad.isPadVisible()) { - currentPad.getController().getView().showBusyView(true); - } + private boolean handleFileDragDropped(Dragboard dragboard) { + final ContentDragOption dragOption = fileHud.getSelectedConnect(); + if (dragOption != null) { + // stop pad if playing + if (currentPad.getContent() != null && currentPad.getStatus().equals(PadStatus.PLAY)) { + currentPad.getContent().stop(); + currentPad.stop(); + } - this.currentPad.setPath(file.toPath()); + dragOption.handleDrop(currentPad, dragboard.getFiles()); - if (currentPad.getController() != null) { - IPadView padView = currentPad.getController().getView(); - padView.setContentView(currentPad); - padView.addDefaultElements(currentPad); - } + if (currentPad.getController() != null) { + final IPadView padView = currentPad.getController().getView(); + padView.setContentView(currentPad); + padView.addDefaultElements(currentPad); } + + return true; } + return false; + } - // Pad DnD - if (db.hasContent(dataFormat)) { - Object data = db.getContent(dataFormat); - if (data instanceof PadIndex) { - PadIndex srcIndex = (PadIndex) data; - PadIndex newIndex = currentPad.getPadIndex(); // Lister ist auf Ziel Pad, daher ist der Index von currentPad - - // Drag handle - PadDragMode mode = padHud.getSelectedPadDragMode(); - success = mode.handle(srcIndex, newIndex, project); - padHud.hide(); - - // Update der Pad Views nach dem DnD - IMainViewController mainViewController = PlayPadPlugin.getInstance().getMainViewController(); - mainViewController.showPage(mainViewController.getPage()); - - if (project.getProjectReference().isSync()) { - CommandManager.execute(Commands.PAD_MOVE); - } + private boolean handlePadDragDropped(Project project, Dragboard dragboard) { + boolean success = false; + + Object data = dragboard.getContent(dataFormat); + if (data instanceof PadIndex) { + PadIndex srcIndex = (PadIndex) data; + PadIndex newIndex = currentPad.getPadIndex(); // Lister ist auf Ziel Pad, daher ist der Index von currentPad + + // Drag handle + PadDragMode mode = padHud.getSelectedPadDragMode(); + success = mode.handle(srcIndex, newIndex, project); + padHud.hide(); + + // Update der Pad Views nach dem DnD + IMainViewController mainViewController = PlayPadPlugin.getInstance().getMainViewController(); + mainViewController.showPage(mainViewController.getPage()); + + if (project.getProjectReference().isSync()) { + CommandManager.execute(Commands.PAD_MOVE); } } - // Event Completion - event.setDropCompleted(success); - event.consume(); + return success; } - private void dragDetacted(MouseEvent event) { + private void dragDetected(MouseEvent event) { if (connect.getEditMode() == DesktopEditMode.DRAG) { if (checkLiveMode()) { return; } - Dragboard dragboard = padView.startDragAndDrop(TransferMode.MOVE); + Dragboard dragboard = padViewNode.startDragAndDrop(TransferMode.MOVE); // Create Snapshot SnapshotParameters parameters = new SnapshotParameters(); parameters.setFill(Color.TRANSPARENT); - WritableImage snapshot = padView.snapshot(parameters, null); + WritableImage snapshot = padViewNode.snapshot(parameters, null); for (int x = 0; x < snapshot.getWidth(); x++) { for (int y = 0; y < snapshot.getHeight(); y++) { Color oldColor = snapshot.getPixelReader().getColor(x, y).darker().darker(); @@ -229,9 +254,7 @@ public class DesktopPadDragListener implements EventHandler<DragEvent> { private boolean checkLiveMode() { GlobalSettings globalSettings = PlayPadPlugin.getInstance().getGlobalSettings(); if (currentPad.getProject() != null) { - if (globalSettings.isLiveMode() && globalSettings.isLiveModeFile() && currentPad.getProject().getActivePlayers() > 0) { - return true; - } + return globalSettings.isLiveMode() && globalSettings.isLiveModeFile() && currentPad.getProject().getActivePlayers() > 0; } return false; } diff --git a/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/pad/DesktopPadView.java b/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/pad/DesktopPadView.java index 1f22e8a009f5d727ee47fbb9a57377ebe1631963..e1489f3445343db9ef7b1b3a46be0e6030a8f690 100644 --- a/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/pad/DesktopPadView.java +++ b/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/pad/DesktopPadView.java @@ -14,10 +14,11 @@ import de.tobias.playpad.pad.PadStatus; import de.tobias.playpad.pad.content.PadContent; import de.tobias.playpad.pad.content.PadContentFactory; import de.tobias.playpad.pad.content.PadContentRegistry; +import de.tobias.playpad.pad.content.Playlistable; import de.tobias.playpad.pad.content.play.Pauseable; import de.tobias.playpad.pad.view.IPadContentView; import de.tobias.playpad.pad.view.IPadView; -import de.tobias.playpad.pad.viewcontroller.IPadViewController; +import de.tobias.playpad.pad.viewcontroller.AbstractPadViewController; import de.tobias.playpad.profile.Profile; import de.tobias.playpad.project.page.PadIndex; import de.tobias.playpad.registry.NoSuchComponentException; @@ -29,6 +30,7 @@ import javafx.beans.property.Property; import javafx.css.PseudoClass; import javafx.geometry.Pos; import javafx.scene.Node; +import javafx.scene.Parent; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.ProgressBar; @@ -42,6 +44,7 @@ public class DesktopPadView implements IPadView { private Label indexLabel; private Label loopLabel; private Label triggerLabel; + private Label playlistLabel; private Label errorLabel; private HBox infoBox; @@ -55,6 +58,7 @@ public class DesktopPadView implements IPadView { private ProgressBar playBar; private Button playButton; private Button pauseButton; + private Button nextButton; private Button stopButton; private Button newButton; private Button settingsButton; @@ -88,6 +92,7 @@ public class DesktopPadView implements IPadView { loopLabel = new PadLabel(new FontIcon(FontAwesomeType.REPEAT)); triggerLabel = new PadLabel(new FontIcon(FontAwesomeType.EXTERNAL_LINK)); + playlistLabel = PadLabel.empty(STYLE_CLASS_PAD_INFO, STYLE_CLASS_PAD_INFO_INDEX); errorLabel = new PadLabel(new FontIcon(FontAwesomeType.WARNING)); infoBox = new PadHBox(5); @@ -106,6 +111,7 @@ public class DesktopPadView implements IPadView { // Buttons playButton = new PadButton(new FontIcon(FontAwesomeType.PLAY), controller); pauseButton = new PadButton(new FontIcon(FontAwesomeType.PAUSE), controller); + nextButton = new PadButton(new FontIcon(FontAwesomeType.STEP_FORWARD), controller); stopButton = new PadButton(new FontIcon(FontAwesomeType.STOP), controller); newButton = new PadButton(new FontIcon(FontAwesomeType.FOLDER_OPEN), controller); settingsButton = new PadButton(new FontIcon(FontAwesomeType.GEAR), controller); @@ -146,11 +152,11 @@ public class DesktopPadView implements IPadView { PadContentFactory connect = registry.getFactory(content.getType()); previewContent = connect.getPadContentPreview(pad, preview); - Node node = previewContent.getNode(); + Parent node = previewContent.getNode(); - // Copy Pseudoclasses + // Copy Pseudo classes for (PseudoClass pseudoClass : superRoot.getPseudoClassStates()) { - node.pseudoClassStateChanged(pseudoClass, true); + NodeWalker.getAllNodes(node).forEach(element -> element.pseudoClassStateChanged(pseudoClass, true)); } preview.getChildren().setAll(node); @@ -165,7 +171,7 @@ public class DesktopPadView implements IPadView { } @Override - public IPadViewController getViewController() { + public AbstractPadViewController getViewController() { return controller; } @@ -212,6 +218,10 @@ public class DesktopPadView implements IPadView { return pauseButton; } + Button getNextButton() { + return nextButton; + } + Button getStopButton() { return stopButton; } @@ -250,7 +260,11 @@ public class DesktopPadView implements IPadView { if (pad.getContent() != null) { if (pad.getContent() instanceof Pauseable) { if (pad.getStatus() == PadStatus.PLAY) { - buttonBox.getChildren().setAll(pauseButton, stopButton, settingsButton); + if (pad.getContent() instanceof Playlistable) { + buttonBox.getChildren().setAll(pauseButton, nextButton, stopButton, settingsButton); + } else { + buttonBox.getChildren().setAll(pauseButton, stopButton, settingsButton); + } } else { buttonBox.getChildren().setAll(playButton, stopButton, settingsButton); } @@ -262,7 +276,7 @@ public class DesktopPadView implements IPadView { } applyStyleClasses(pad.getPadIndex()); } - infoBox.getChildren().setAll(indexLabel, loopLabel, triggerLabel, errorLabel, timeLabel); + infoBox.getChildren().setAll(indexLabel, loopLabel, triggerLabel, playlistLabel, errorLabel, timeLabel); // Buttons unten Full Width buttonBox.prefWidthProperty().bind(superRoot.widthProperty()); @@ -318,6 +332,10 @@ public class DesktopPadView implements IPadView { setContentView(null); } + public Label getPlaylistLabel() { + return playlistLabel; + } + @Override public void setPlaybarVisible(boolean visible) { playBar.setVisible(visible); diff --git a/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/pad/DesktopPadViewController.java b/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/pad/DesktopPadViewController.java index 9da5a3879097a79a71b00405ef39fd4d7a9df3ae..fd9acc77f4555699721cdea533a759b8a673a3fd 100644 --- a/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/pad/DesktopPadViewController.java +++ b/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/pad/DesktopPadViewController.java @@ -9,10 +9,11 @@ import de.tobias.playpad.layout.desktop.listener.PadNewContentListener; import de.tobias.playpad.pad.Pad; import de.tobias.playpad.pad.PadStatus; import de.tobias.playpad.pad.TimeMode; +import de.tobias.playpad.pad.content.Playlistable; import de.tobias.playpad.pad.content.play.Durationable; import de.tobias.playpad.pad.listener.*; import de.tobias.playpad.pad.view.IPadView; -import de.tobias.playpad.pad.viewcontroller.IPadViewController; +import de.tobias.playpad.pad.viewcontroller.AbstractPadViewController; import de.tobias.playpad.profile.Profile; import de.tobias.playpad.profile.ProfileSettings; import de.tobias.playpad.registry.NoSuchComponentException; @@ -23,22 +24,21 @@ import de.tobias.playpad.viewcontroller.option.pad.PadSettingsViewController; import javafx.beans.value.ChangeListener; import javafx.event.ActionEvent; import javafx.event.EventHandler; -import javafx.stage.Stage; import javafx.util.Duration; -public class DesktopPadViewController implements IPadViewController, EventHandler<ActionEvent> { +public class DesktopPadViewController extends AbstractPadViewController implements EventHandler<ActionEvent> { public static final String OPEN_FOLDER = "openFolder"; private static final String DURATION_FORMAT = "%d:%02d"; - private DesktopPadView padView; + private final DesktopPadView padView; private Pad pad; - private PadLockedListener padLockedListener; - private PadStatusListener padStatusListener; - private PadContentListener padContentListener; - private PadDurationListener padDurationListener; - private IPadPositionListener padPositionListener; + private final PadLockedListener padLockedListener; + private final PadStatusListener padStatusListener; + private final PadContentListener padContentListener; + private final PadDurationListener padDurationListener; + private final IPadPositionListener padPositionListener; private DesktopPadDragListener padDragListener; @@ -84,6 +84,8 @@ public class DesktopPadViewController implements IPadViewController, EventHandle padView.loopLabelVisibleProperty().bind(pad.getPadSettings().loopProperty()); padView.setTriggerLabelActive(pad.getPadSettings().hasTriggerItems()); + updatePlaylistLabelBinding(pad); + // Update Listener padContentListener.setPad(pad); padPositionListener.setPad(pad); @@ -94,7 +96,7 @@ public class DesktopPadViewController implements IPadViewController, EventHandle // Initial Listener call with new data padContentListener.changed(null, null, pad.getContent()); // Add Duration listener - padStatusListener.changed(null, null, pad.getStatus()); + padStatusListener.changed(null, null, pad.getStatus()); // Show correct pseudo classes ... // Add Drag and Drop Listener padDragListener = new DesktopPadDragListener(pad, padView, connect); @@ -117,6 +119,9 @@ public class DesktopPadViewController implements IPadViewController, EventHandle padView.setTriggerLabelActive(false); padView.loopLabelVisibleProperty().unbind(); + padView.getPlaylistLabel().textProperty().unbind(); + padView.getPlaylistLabel().setText(""); + // Remove Bindings & Listener pad.contentProperty().removeListener(padContentListener); pad.statusProperty().removeListener(padStatusListener); @@ -152,6 +157,8 @@ public class DesktopPadViewController implements IPadViewController, EventHandle onPlay(); } else if (event.getSource() == padView.getPauseButton()) { onPause(); + } else if (event.getSource() == padView.getNextButton()) { + onNext(); } else if (event.getSource() == padView.getStopButton()) { onStop(); } else if (event.getSource() == padView.getNewButton()) { @@ -179,6 +186,12 @@ public class DesktopPadViewController implements IPadViewController, EventHandle } } + private void onNext() { + if (pad.getContent() instanceof Playlistable) { + ((Playlistable) pad.getContent()).next(); + } + } + private void onStop() { if (pad.getContent() != null) { pad.setStatus(PadStatus.STOP); @@ -195,15 +208,11 @@ public class DesktopPadViewController implements IPadViewController, EventHandle IMainViewController mvc = PlayPadPlugin.getInstance().getMainViewController(); if (mvc != null) { - if (pad.getProject() != null) { - if (settings.isLiveMode() && settings.isLiveModeSettings() && pad.getProject().getActivePlayers() > 0) { - return; - } + if (pad.getProject() != null && settings.isLiveMode() && settings.isLiveModeSettings() && pad.getProject().getActivePlayers() > 0) { + return; } - Stage owner = mvc.getStage(); - - PadSettingsViewController padSettingsViewController = new PadSettingsViewController(pad, owner); + PadSettingsViewController padSettingsViewController = new PadSettingsViewController(pad, mvc); padSettingsViewController.getStageContainer().ifPresent(nvcStage -> nvcStage.addCloseHook(() -> { if (padView != null && pad != null) padView.setTriggerLabelActive(pad.getPadSettings().hasTriggerItems()); diff --git a/PlayWall/src/main/java/de/tobias/playpad/layout/touch/pad/TouchPadView.java b/PlayWall/src/main/java/de/tobias/playpad/layout/touch/pad/TouchPadView.java index 0ae8de45f92ab9a27cbeb1c642ad083763efbc9d..3c220b8ba255b641ea8355d2cc818418e90e1a5f 100644 --- a/PlayWall/src/main/java/de/tobias/playpad/layout/touch/pad/TouchPadView.java +++ b/PlayWall/src/main/java/de/tobias/playpad/layout/touch/pad/TouchPadView.java @@ -14,7 +14,7 @@ import de.tobias.playpad.pad.content.PadContent; import de.tobias.playpad.pad.content.PadContentFactory; import de.tobias.playpad.pad.view.IPadContentView; import de.tobias.playpad.pad.view.IPadView; -import de.tobias.playpad.pad.viewcontroller.IPadViewController; +import de.tobias.playpad.pad.viewcontroller.AbstractPadViewController; import de.tobias.playpad.profile.Profile; import de.tobias.playpad.project.page.PadIndex; import de.tobias.playpad.registry.NoSuchComponentException; @@ -38,6 +38,7 @@ public class TouchPadView implements IPadView { private Label indexLabel; private Label loopLabel; private Label triggerLabel; + private Label playlistLabel; private Label errorLabel; private HBox infoBox; @@ -77,6 +78,7 @@ public class TouchPadView implements IPadView { loopLabel = new PadLabel(new FontIcon(FontAwesomeType.REPEAT)); triggerLabel = new PadLabel(new FontIcon(FontAwesomeType.EXTERNAL_LINK)); + playlistLabel = PadLabel.empty(STYLE_CLASS_PAD_INFO, STYLE_CLASS_PAD_INFO_INDEX); errorLabel = new PadLabel(new FontIcon(FontAwesomeType.WARNING)); infoBox = new PadHBox(5); @@ -147,7 +149,7 @@ public class TouchPadView implements IPadView { } @Override - public IPadViewController getViewController() { + public AbstractPadViewController getViewController() { return controller; } @@ -212,7 +214,7 @@ public class TouchPadView implements IPadView { @Override public void addDefaultElements(Pad pad) { - infoBox.getChildren().setAll(indexLabel, loopLabel, triggerLabel, errorLabel, timeLabel); + infoBox.getChildren().setAll(indexLabel, loopLabel, triggerLabel, playlistLabel, errorLabel, timeLabel); // alle Labels in der InfoBox sollen die gleiche Höhe haben, damit die Icons auf gleicher höhe sind for (Node child : infoBox.getChildren()) { @@ -258,6 +260,10 @@ public class TouchPadView implements IPadView { setContentView(null); } + public Label getPlaylistLabel() { + return playlistLabel; + } + @Override public void setPlaybarVisible(boolean visible) { playBar.setVisible(visible); diff --git a/PlayWall/src/main/java/de/tobias/playpad/layout/touch/pad/TouchPadViewController.java b/PlayWall/src/main/java/de/tobias/playpad/layout/touch/pad/TouchPadViewController.java index d6a75430cb0063cd1191e25e9a2499c2f7080f92..c43b9679075c400a0e949b80e2b9ab64e28d4f50 100644 --- a/PlayWall/src/main/java/de/tobias/playpad/layout/touch/pad/TouchPadViewController.java +++ b/PlayWall/src/main/java/de/tobias/playpad/layout/touch/pad/TouchPadViewController.java @@ -7,7 +7,7 @@ import de.tobias.playpad.pad.TimeMode; import de.tobias.playpad.pad.content.play.Durationable; import de.tobias.playpad.pad.listener.*; import de.tobias.playpad.pad.view.IPadView; -import de.tobias.playpad.pad.viewcontroller.IPadViewController; +import de.tobias.playpad.pad.viewcontroller.AbstractPadViewController; import de.tobias.playpad.profile.Profile; import de.tobias.playpad.profile.ProfileSettings; import javafx.beans.value.ChangeListener; @@ -18,7 +18,7 @@ import javafx.scene.input.MouseEvent; import javafx.scene.input.TouchEvent; import javafx.util.Duration; -public class TouchPadViewController implements IPadViewController, EventHandler<Event> { +public class TouchPadViewController extends AbstractPadViewController implements EventHandler<Event> { protected static final String CURRENT_PAGE_BUTTON = "current-page-button"; private static final String DURATION_FORMAT = "%d:%02d"; @@ -70,11 +70,13 @@ public class TouchPadViewController implements IPadViewController, EventHandler< padView.loopLabelVisibleProperty().bind(pad.getPadSettings().loopProperty()); padView.setTriggerLabelActive(pad.getPadSettings().hasTriggerItems()); + updatePlaylistLabelBinding(pad); + // Update Listener padContentListener.setPad(pad); padPositionListener.setPad(pad); - // Pad Content Chnage + // Pad Content Change pad.contentProperty().addListener(padContentListener); // Pad Status Change pad.statusProperty().addListener(padStatusListener); @@ -98,6 +100,9 @@ public class TouchPadViewController implements IPadViewController, EventHandler< padView.clearPreviewContent(); padView.clearTime(); + padView.getPlaylistLabel().textProperty().unbind(); + padView.getPlaylistLabel().setText(""); + padView.setTriggerLabelActive(false); padView.loopLabelVisibleProperty().unbind(); diff --git a/PlayWall/src/main/java/de/tobias/playpad/pad/content/AudioPadContentFactory.java b/PlayWall/src/main/java/de/tobias/playpad/pad/content/AudioPadContentFactory.java index d60a59d949a82adbb94f117600f4d9e6fd95da51..8e5f4339e3e2107bab28ee4ba9c591fcabe1c138 100644 --- a/PlayWall/src/main/java/de/tobias/playpad/pad/content/AudioPadContentFactory.java +++ b/PlayWall/src/main/java/de/tobias/playpad/pad/content/AudioPadContentFactory.java @@ -1,16 +1,11 @@ package de.tobias.playpad.pad.content; import de.tobias.playpad.pad.Pad; +import de.tobias.playpad.pad.preview.PadTextPreview; import de.tobias.playpad.pad.view.IPadContentView; import de.tobias.playpad.viewcontroller.option.ProfileSettingsTabViewController; import de.tobias.playpad.viewcontroller.option.profile.AudioTabViewController; -import javafx.geometry.Pos; -import javafx.scene.Node; -import javafx.scene.control.Label; import javafx.scene.layout.Pane; -import javafx.scene.layout.Priority; -import javafx.scene.layout.VBox; -import javafx.scene.text.TextAlignment; public class AudioPadContentFactory extends PadContentFactory { @@ -33,7 +28,7 @@ public class AudioPadContentFactory extends PadContentFactory { @Override public IPadContentView getPadContentPreview(Pad pad, Pane parentNode) { if (pad.getContent() != null) { - return new AudioContentView(pad, parentNode); + return new PadTextPreview(pad, parentNode); } else { return null; } @@ -43,32 +38,4 @@ public class AudioPadContentFactory extends PadContentFactory { public ProfileSettingsTabViewController getSettingsTabViewController(boolean activePlayer) { return new AudioTabViewController(activePlayer); } - - private static class AudioContentView implements IPadContentView { - - private Label nameLabel; - - AudioContentView(Pad pad, Pane parentNode) { - nameLabel = new Label(); - nameLabel.textProperty().bind(pad.nameProperty()); - - nameLabel.setWrapText(true); - nameLabel.setAlignment(Pos.CENTER); - nameLabel.setTextAlignment(TextAlignment.CENTER); - - nameLabel.prefWidthProperty().bind(parentNode.widthProperty()); - nameLabel.setMaxHeight(Double.MAX_VALUE); - VBox.setVgrow(nameLabel, Priority.ALWAYS); - } - - @Override - public Node getNode() { - return nameLabel; - } - - @Override - public void deInit() { - nameLabel.textProperty().unbind(); - } - } } diff --git a/PlayWall/src/main/java/de/tobias/playpad/pad/drag/PlaylistDragOption.java b/PlayWall/src/main/java/de/tobias/playpad/pad/drag/PlaylistDragOption.java new file mode 100644 index 0000000000000000000000000000000000000000..69e86ec2c6f8c2b9c437daffec996dc9aea8213a --- /dev/null +++ b/PlayWall/src/main/java/de/tobias/playpad/pad/drag/PlaylistDragOption.java @@ -0,0 +1,40 @@ +package de.tobias.playpad.pad.drag; + +import de.thecodelabs.utils.ui.icon.FontAwesomeType; +import de.thecodelabs.utils.ui.icon.FontIcon; +import de.thecodelabs.utils.util.Localization; +import de.tobias.playpad.pad.Pad; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.scene.Node; + +import java.io.File; +import java.util.List; + +public class PlaylistDragOption implements ContentDragOption { + + private final StringProperty displayProperty = new SimpleStringProperty(Localization.getString("DndMode.Playlist")); + + @Override + public void handleDrop(Pad currentPad, List<File> files) { + if (currentPad.isPadVisible()) { + currentPad.getController().getView().showBusyView(true); + } + + for (File file : files) { + currentPad.addPath(file.toPath()); + } + } + + @Override + public StringProperty displayProperty() { + return displayProperty; + } + + @Override + public Node getGraphics() { + final FontIcon fontIcon = new FontIcon(FontAwesomeType.PLUS_CIRCLE); + fontIcon.setSize(30); + return fontIcon; + } +} diff --git a/PlayWall/src/main/java/de/tobias/playpad/pad/listener/PadContentListener.java b/PlayWall/src/main/java/de/tobias/playpad/pad/listener/PadContentListener.java index 0b88a71ca642f337b7c892cf68bc04d3a2efc9ab..20c58f70592345eadb08fc634bcf34a7824951ec 100644 --- a/PlayWall/src/main/java/de/tobias/playpad/pad/listener/PadContentListener.java +++ b/PlayWall/src/main/java/de/tobias/playpad/pad/listener/PadContentListener.java @@ -3,16 +3,16 @@ package de.tobias.playpad.pad.listener; import de.tobias.playpad.pad.Pad; import de.tobias.playpad.pad.content.PadContent; import de.tobias.playpad.pad.content.play.Durationable; -import de.tobias.playpad.pad.viewcontroller.IPadViewController; +import de.tobias.playpad.pad.viewcontroller.AbstractPadViewController; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; public class PadContentListener implements ChangeListener<PadContent> { private Pad pad; - private IPadViewController controller; + private final AbstractPadViewController controller; - public PadContentListener(IPadViewController controller) { + public PadContentListener(AbstractPadViewController controller) { this.controller = controller; } @@ -49,5 +49,7 @@ public class PadContentListener implements ChangeListener<PadContent> { } else { controller.getView().setPlaybarVisible(false); } + + controller.updatePlaylistLabelBinding(pad); } } diff --git a/PlayWall/src/main/java/de/tobias/playpad/pad/listener/PadDurationListener.java b/PlayWall/src/main/java/de/tobias/playpad/pad/listener/PadDurationListener.java index ce208522f99ec464cbe6996109ad0a4b3c9210d5..a364fed1a5f5f6996c4885374d1b8acd70faa936 100644 --- a/PlayWall/src/main/java/de/tobias/playpad/pad/listener/PadDurationListener.java +++ b/PlayWall/src/main/java/de/tobias/playpad/pad/listener/PadDurationListener.java @@ -1,15 +1,15 @@ package de.tobias.playpad.pad.listener; -import de.tobias.playpad.pad.viewcontroller.IPadViewController; +import de.tobias.playpad.pad.viewcontroller.AbstractPadViewController; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.util.Duration; public class PadDurationListener implements ChangeListener<Duration> { - private IPadViewController controller; + private AbstractPadViewController controller; - public PadDurationListener(IPadViewController controller) { + public PadDurationListener(AbstractPadViewController controller) { this.controller = controller; } diff --git a/PlayWall/src/main/java/de/tobias/playpad/pad/listener/PadLockedListener.java b/PlayWall/src/main/java/de/tobias/playpad/pad/listener/PadLockedListener.java index 702951b06dab11e634e5c4aec827a06c98638f4e..e94995dda657050904571c0d88dec486b4d62dfa 100644 --- a/PlayWall/src/main/java/de/tobias/playpad/pad/listener/PadLockedListener.java +++ b/PlayWall/src/main/java/de/tobias/playpad/pad/listener/PadLockedListener.java @@ -1,14 +1,14 @@ package de.tobias.playpad.pad.listener; -import de.tobias.playpad.pad.viewcontroller.IPadViewController; +import de.tobias.playpad.pad.viewcontroller.AbstractPadViewController; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; public class PadLockedListener implements ChangeListener<Boolean> { - private IPadViewController controller; + private AbstractPadViewController controller; - public PadLockedListener(IPadViewController controller) { + public PadLockedListener(AbstractPadViewController controller) { this.controller = controller; } diff --git a/PlayWall/src/main/java/de/tobias/playpad/pad/listener/PadPositionListener.java b/PlayWall/src/main/java/de/tobias/playpad/pad/listener/PadPositionListener.java index 579fe5a9dfb6fbac7a207b2f67b09cfce5c74800..5326b5f914229d7a4e1b9499bb44c01234d00ecf 100644 --- a/PlayWall/src/main/java/de/tobias/playpad/pad/listener/PadPositionListener.java +++ b/PlayWall/src/main/java/de/tobias/playpad/pad/listener/PadPositionListener.java @@ -13,7 +13,7 @@ import de.tobias.playpad.pad.PadStatus; import de.tobias.playpad.pad.content.PadContent; import de.tobias.playpad.pad.content.play.Durationable; import de.tobias.playpad.pad.fade.Fadeable; -import de.tobias.playpad.pad.viewcontroller.IPadViewController; +import de.tobias.playpad.pad.viewcontroller.AbstractPadViewController; import de.tobias.playpad.profile.Profile; import javafx.beans.value.ObservableValue; import javafx.util.Duration; @@ -21,9 +21,9 @@ import javafx.util.Duration; public class PadPositionListener implements Runnable, IPadPositionListener { private Pad pad; - private IPadViewController controller; + private AbstractPadViewController controller; - public PadPositionListener(IPadViewController controller) { + public PadPositionListener(AbstractPadViewController controller) { this.controller = controller; } diff --git a/PlayWall/src/main/java/de/tobias/playpad/pad/listener/PadStatusListener.java b/PlayWall/src/main/java/de/tobias/playpad/pad/listener/PadStatusListener.java index bda5270de706c8c4ecfece808faaa2780eb11170..061cc894962680419298d76f8b326c825afb6770 100644 --- a/PlayWall/src/main/java/de/tobias/playpad/pad/listener/PadStatusListener.java +++ b/PlayWall/src/main/java/de/tobias/playpad/pad/listener/PadStatusListener.java @@ -3,16 +3,16 @@ package de.tobias.playpad.pad.listener; import de.thecodelabs.midi.Mapping; import de.tobias.playpad.action.actions.CartAction; import de.tobias.playpad.pad.PadStatus; -import de.tobias.playpad.pad.viewcontroller.IPadViewController; +import de.tobias.playpad.pad.viewcontroller.AbstractPadViewController; import de.tobias.playpad.view.PseudoClasses; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; public class PadStatusListener implements ChangeListener<PadStatus> { - private IPadViewController controller; + private AbstractPadViewController controller; - public PadStatusListener(IPadViewController controller) { + public PadStatusListener(AbstractPadViewController controller) { this.controller = controller; } diff --git a/PlayWall/src/main/java/de/tobias/playpad/trigger/CartTriggerItemFactory.java b/PlayWall/src/main/java/de/tobias/playpad/trigger/CartTriggerItemFactory.java index 98290adbcd0df99adf16698fe28258999e072a94..e103e69319bbd4457628bb024cc4f812e00d2c66 100644 --- a/PlayWall/src/main/java/de/tobias/playpad/trigger/CartTriggerItemFactory.java +++ b/PlayWall/src/main/java/de/tobias/playpad/trigger/CartTriggerItemFactory.java @@ -6,6 +6,7 @@ import de.tobias.playpad.Strings; import de.tobias.playpad.tigger.Trigger; import de.tobias.playpad.tigger.TriggerItem; import de.tobias.playpad.tigger.TriggerItemFactory; +import de.tobias.playpad.viewcontroller.main.IMainViewController; import de.tobias.playpad.viewcontroller.option.pad.trigger.CartTriggerViewController; public class CartTriggerItemFactory extends TriggerItemFactory { @@ -20,8 +21,8 @@ public class CartTriggerItemFactory extends TriggerItemFactory { } @Override - public NVC getSettingsController(TriggerItem item) { - return new CartTriggerViewController((CartTriggerItem) item); + public NVC getSettingsController(TriggerItem item, IMainViewController mainViewController) { + return new CartTriggerViewController((CartTriggerItem) item, mainViewController); } @Override diff --git a/PlayWall/src/main/java/de/tobias/playpad/trigger/TriggerDisplayable.java b/PlayWall/src/main/java/de/tobias/playpad/trigger/TriggerDisplayable.java index 434a17a2577ee8aee5a27b1a2850134362e350d0..e6a6a6aa5d3748b2cc348230870c16ac1f5e263b 100644 --- a/PlayWall/src/main/java/de/tobias/playpad/trigger/TriggerDisplayable.java +++ b/PlayWall/src/main/java/de/tobias/playpad/trigger/TriggerDisplayable.java @@ -10,7 +10,7 @@ import javafx.beans.property.StringProperty; public class TriggerDisplayable implements Displayable { - private Trigger trigger; + private final Trigger trigger; public TriggerDisplayable(Trigger trigger) { this.trigger = trigger; @@ -21,7 +21,7 @@ public class TriggerDisplayable implements Displayable { return trigger; } - private StringProperty displayable = new SimpleStringProperty(); + private final StringProperty displayable = new SimpleStringProperty(); @Override public StringProperty displayProperty() { diff --git a/PlayWall/src/main/java/de/tobias/playpad/trigger/VolumeTriggerItemFactory.java b/PlayWall/src/main/java/de/tobias/playpad/trigger/VolumeTriggerItemFactory.java index 73d2447a29858d8ba7ea630749aa89d8cec357dc..fa908a635f0a02ded7f7e3c4518a21a6c5efef05 100644 --- a/PlayWall/src/main/java/de/tobias/playpad/trigger/VolumeTriggerItemFactory.java +++ b/PlayWall/src/main/java/de/tobias/playpad/trigger/VolumeTriggerItemFactory.java @@ -6,6 +6,7 @@ import de.tobias.playpad.Strings; import de.tobias.playpad.tigger.Trigger; import de.tobias.playpad.tigger.TriggerItem; import de.tobias.playpad.tigger.TriggerItemFactory; +import de.tobias.playpad.viewcontroller.main.IMainViewController; import de.tobias.playpad.viewcontroller.option.pad.trigger.VolumeTriggerViewController; public class VolumeTriggerItemFactory extends TriggerItemFactory { @@ -20,8 +21,8 @@ public class VolumeTriggerItemFactory extends TriggerItemFactory { } @Override - public NVC getSettingsController(TriggerItem item) { - return new VolumeTriggerViewController((VolumeTriggerItem) item); + public NVC getSettingsController(TriggerItem item, IMainViewController mainViewController) { + return new VolumeTriggerViewController((VolumeTriggerItem) item, mainViewController); } @Override diff --git a/PlayWall/src/main/java/de/tobias/playpad/util/ListSerializer.java b/PlayWall/src/main/java/de/tobias/playpad/util/ListSerializer.java new file mode 100644 index 0000000000000000000000000000000000000000..155d9bdcf0638bd678fb6dae9ab45799963f68e6 --- /dev/null +++ b/PlayWall/src/main/java/de/tobias/playpad/util/ListSerializer.java @@ -0,0 +1,35 @@ +package de.tobias.playpad.util; + +import de.thecodelabs.storage.settings.UserDefaults; +import org.dom4j.Element; + +import java.util.ArrayList; +import java.util.List; + +public class ListSerializer implements UserDefaults.Serializer<ArrayList> { + + private static final String LIST_ITEM = "item"; + + @Override + public ArrayList get(Element element) { + ArrayList<Object> list = new ArrayList<>(); + for (Element child : element.elements(LIST_ITEM)) { + final Object o = UserDefaults.loadElement(child); + list.add(o); + } + return list; + } + + @Override + public void set(Object o, Element element) { + if (o instanceof List) { + //noinspection unchecked + final List<Object> list = (List<Object>) o; + + for (Object item : list) { + final Element childElement = element.addElement(LIST_ITEM); + UserDefaults.save(childElement, item, null); + } + } + } +} diff --git a/PlayWall/src/main/java/de/tobias/playpad/view/FileDragOptionView.java b/PlayWall/src/main/java/de/tobias/playpad/view/FileDragOptionView.java index 1510b4cfe33d5043e00289a547ccae68be45cac2..38270ca6887aaa067025135443761d2215c55706 100644 --- a/PlayWall/src/main/java/de/tobias/playpad/view/FileDragOptionView.java +++ b/PlayWall/src/main/java/de/tobias/playpad/view/FileDragOptionView.java @@ -1,6 +1,7 @@ package de.tobias.playpad.view; import de.tobias.playpad.pad.content.PadContentFactory; +import de.tobias.playpad.pad.drag.ContentDragOption; import javafx.animation.FadeTransition; import javafx.animation.ParallelTransition; import javafx.animation.ScaleTransition; @@ -14,7 +15,7 @@ import javafx.scene.layout.*; import javafx.scene.paint.Color; import javafx.scene.text.TextAlignment; -import java.util.Set; +import java.util.Collection; import java.util.function.Consumer; public class FileDragOptionView implements PadContentFactory.PadContentTypeChooser { @@ -76,9 +77,9 @@ public class FileDragOptionView implements PadContentFactory.PadContentTypeChoos return parallelTransition; } - private PadContentFactory selectedConnect; + private ContentDragOption selectedConnect; - public void showOptions(Set<PadContentFactory> options) { + public void showOptions(Collection<? extends ContentDragOption> options) { if (!parent.getChildren().contains(optionPane)) { selectedConnect = null; @@ -125,7 +126,7 @@ public class FileDragOptionView implements PadContentFactory.PadContentTypeChoos } - public void showOptions(Set<PadContentFactory> options, Consumer<PadContentFactory> onFinish) { + public void showOptions(Collection<PadContentFactory> options, Consumer<PadContentFactory> onFinish) { showOptions(options); for (Node node : optionPane.getChildren()) { @@ -143,7 +144,7 @@ public class FileDragOptionView implements PadContentFactory.PadContentTypeChoos } } - public PadContentFactory getSelectedConnect() { + public ContentDragOption getSelectedConnect() { return selectedConnect; } diff --git a/PlayWall/src/main/java/de/tobias/playpad/view/PadDragOptionView.java b/PlayWall/src/main/java/de/tobias/playpad/view/PadDragOptionView.java index 205250172cbe517acfb7cb51d1f052de96eae133..7dac34cde41953601b45c78688458d1418ed47ec 100644 --- a/PlayWall/src/main/java/de/tobias/playpad/view/PadDragOptionView.java +++ b/PlayWall/src/main/java/de/tobias/playpad/view/PadDragOptionView.java @@ -100,7 +100,7 @@ public class PadDragOptionView { parent.getChildren().add(optionPane); optionPane.getChildren().clear(); - for (PadDragMode connect : options.stream().sorted().toArray(value -> new PadDragMode[value])) { + for (PadDragMode connect : options.stream().sorted().toArray(PadDragMode[]::new)) { Label label = new Label(); label.getStyleClass().add("dnd-file-option"); diff --git a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/main/MainViewController.java b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/main/MainViewController.java index b2bf4ab78fee9e28884c81cd21fb457db7f1f677..ec8f51a3dfc669a93d119794e71e1f9b2d29ee48 100644 --- a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/main/MainViewController.java +++ b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/main/MainViewController.java @@ -363,6 +363,7 @@ public class MainViewController extends NVC implements IMainViewController, Noti } } } + Platform.exit(); return true; } diff --git a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/GeneralPadTabViewController.java b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/GeneralPadTabViewController.java index d35275146955d05c9d1625ab4263b1c6735decc9..4e40233ca501ef4edc4c8f2a3c1aca28d042dadb 100644 --- a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/GeneralPadTabViewController.java +++ b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/GeneralPadTabViewController.java @@ -9,6 +9,7 @@ import de.tobias.playpad.pad.PadSettings; import de.tobias.playpad.pad.PadStatus; import de.tobias.playpad.pad.TimeMode; import de.tobias.playpad.pad.content.PadContentFactory; +import de.tobias.playpad.pad.content.Playlistable; import de.tobias.playpad.viewcontroller.PadSettingsTabViewController; import de.tobias.playpad.viewcontroller.cell.EnumCell; import javafx.beans.binding.Bindings; @@ -16,12 +17,15 @@ import javafx.beans.value.ChangeListener; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.scene.control.*; +import javafx.scene.layout.VBox; import javafx.stage.Stage; import java.util.Optional; public class GeneralPadTabViewController extends PadSettingsTabViewController { + @FXML + private VBox mediaRootBox; @FXML private Label pathLabel; @FXML @@ -55,6 +59,11 @@ public class GeneralPadTabViewController extends PadSettingsTabViewController { } showPathButton.disableProperty().bind(Bindings.isEmpty(pad.getPaths())); + // Disable media section for playlists + if (pad.getContent() instanceof Playlistable) { + mediaRootBox.setDisable(true); + } + if (pad.getStatus() == PadStatus.PLAY || pad.getStatus() == PadStatus.PAUSE) { deleteButton.setDisable(true); } diff --git a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/PadSettingsViewController.java b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/PadSettingsViewController.java index 9cb33f084def3bdefe0a5610a70e8fc4bf146350..efccf7b0e68031570f6c1e06d0a82ab2de46366f 100644 --- a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/PadSettingsViewController.java +++ b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/PadSettingsViewController.java @@ -10,16 +10,17 @@ import de.tobias.playpad.pad.Pad; import de.tobias.playpad.pad.PadStatus; import de.tobias.playpad.pad.content.PadContentFactory; import de.tobias.playpad.pad.content.PadContentRegistry; +import de.tobias.playpad.pad.content.Playlistable; import de.tobias.playpad.registry.NoSuchComponentException; import de.tobias.playpad.viewcontroller.IPadSettingsViewController; import de.tobias.playpad.viewcontroller.PadSettingsTabViewController; +import de.tobias.playpad.viewcontroller.main.IMainViewController; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.scene.control.Button; import javafx.scene.control.Tab; import javafx.scene.control.TabPane; import javafx.stage.Stage; -import javafx.stage.Window; import java.util.ArrayList; import java.util.List; @@ -35,14 +36,17 @@ public class PadSettingsViewController extends NVC implements IPadSettingsViewCo @FXML private Button finishButton; - public PadSettingsViewController(Pad pad, Window owner) { + public PadSettingsViewController(Pad pad, IMainViewController mainViewController) { load("view/option/pad", "PadSettingsView", Localization.getBundle()); this.pad = pad; addTab(new GeneralPadTabViewController(pad)); + if (pad.getContent() instanceof Playlistable) { + addTab(new PlaylistTabViewController(pad)); + } addTab(new DesignPadTabViewController(pad)); addTab(new PlayerPadTabViewController(pad)); - addTab(new TriggerPadTabViewController(pad)); + addTab(new TriggerPadTabViewController(pad, mainViewController)); if (pad.getContent() != null) { try { @@ -61,7 +65,7 @@ public class PadSettingsViewController extends NVC implements IPadSettingsViewCo } NVCStage nvcStage = applyViewControllerToStage(); - nvcStage.initOwner(owner); + nvcStage.initOwner(mainViewController.getStage()); nvcStage.addCloseHook(this::onFinish); addCloseKeyShortcut(() -> finishButton.fire()); @@ -85,7 +89,7 @@ public class PadSettingsViewController extends NVC implements IPadSettingsViewCo stage.getIcons().add(PlayPadPlugin.getInstance().getIcon()); stage.setMinWidth(650); - stage.setMinHeight(550); + stage.setMinHeight(600); PlayPadPlugin.styleable().applyStyle(stage); } diff --git a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/PlaylistTabViewController.java b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/PlaylistTabViewController.java new file mode 100644 index 0000000000000000000000000000000000000000..0a80e87d3169c61af24b9a8fdaf0f233f6cf2979 --- /dev/null +++ b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/PlaylistTabViewController.java @@ -0,0 +1,165 @@ +package de.tobias.playpad.viewcontroller.option.pad; + +import de.thecodelabs.utils.application.system.NativeApplication; +import de.thecodelabs.utils.ui.icon.FontAwesomeType; +import de.thecodelabs.utils.ui.icon.FontIcon; +import de.thecodelabs.utils.util.Localization; +import de.tobias.playpad.PlayPadPlugin; +import de.tobias.playpad.Strings; +import de.tobias.playpad.layout.desktop.listener.PadNewContentListener; +import de.tobias.playpad.pad.Pad; +import de.tobias.playpad.pad.content.PadContentFactory; +import de.tobias.playpad.pad.content.PadContentRegistry; +import de.tobias.playpad.pad.content.Playlistable; +import de.tobias.playpad.pad.mediapath.MediaPath; +import de.tobias.playpad.viewcontroller.PadSettingsTabViewController; +import javafx.beans.binding.Bindings; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; + +import java.io.File; +import java.util.Collections; +import java.util.List; + +public class PlaylistTabViewController extends PadSettingsTabViewController { + + @FXML + private ListView<MediaPath> mediaPathListView; + + @FXML + private Button addButton; + @FXML + private Button upButton; + @FXML + private Button downButton; + + @FXML + private Label pathLabel; + @FXML + private Button deleteButton; + @FXML + private Button showFileButton; + + private final Pad pad; + + public PlaylistTabViewController(Pad pad) { + this.pad = pad; + load("view/option/pad", "PlaylistTab", Localization.getBundle()); + + mediaPathListView.setItems(pad.getPaths()); + initButtons(); + } + + @Override + public void init() { + addButton.setGraphic(new FontIcon(FontAwesomeType.PLUS)); + upButton.setGraphic(new FontIcon(FontAwesomeType.ARROW_UP)); + downButton.setGraphic(new FontIcon(FontAwesomeType.ARROW_DOWN)); + + mediaPathListView.setCellFactory(param -> new ListCell<MediaPath>() { + @Override + protected void updateItem(MediaPath item, boolean empty) { + super.updateItem(item, empty); + if (!empty) { + setText(item.getFileName()); + final Playlistable playlist = (Playlistable) pad.getContent(); + if (!playlist.isLoaded(item)) { + setGraphic(new FontIcon(FontAwesomeType.WARNING)); + } else { + setGraphic(null); + } + } else { + setText(""); + setGraphic(null); + } + } + }); + + pathLabel.setText(Localization.getString("padSettings.gen.label.media.empty")); + mediaPathListView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> { + if (newValue != null) { + pathLabel.setText(newValue.getPath().toString()); + } else { + pathLabel.setText(Localization.getString("padSettings.gen.label.media.empty")); + } + }); + } + + private void initButtons() { + upButton.disableProperty() + .bind(mediaPathListView.getSelectionModel().selectedItemProperty().isNull() + .or(mediaPathListView.getSelectionModel().selectedIndexProperty().isEqualTo(0))); + downButton.disableProperty() + .bind(mediaPathListView.getSelectionModel().selectedItemProperty().isNull() + .or(mediaPathListView.getSelectionModel().selectedIndexProperty().isEqualTo(Bindings.size(mediaPathListView.getItems()).subtract(1)))); + + deleteButton.disableProperty().bind(mediaPathListView.getSelectionModel().selectedItemProperty().isNull()); + showFileButton.disableProperty().bind(mediaPathListView.getSelectionModel().selectedItemProperty().isNull()); + } + + @Override + public String getName() { + return Localization.getString(Strings.UI_WINDOW_PAD_SETTINGS_PLAYLIST_TITLE); + } + + @Override + public void loadSettings(Pad pad) { + // Not implemented + } + + @Override + public void saveSettings(Pad pad) { + // Not implemented + } + + @FXML + public void onAddHandler(ActionEvent event) { + final PadContentRegistry padContentRegistry = PlayPadPlugin.getRegistries().getPadContents(); + final PadContentFactory contentFactory = padContentRegistry.getFactory(pad.getContent().getType()); + + final PadNewContentListener padNewContentListener = new PadNewContentListener(pad); + final List<File> files = padNewContentListener.showMediaOpenFileChooser(event, contentFactory.getSupportedTypes(), true); + + if (files != null) { + for (File file : files) { + pad.addPath(file.toPath()); + } + } + } + + @FXML + public void onUpHandler(ActionEvent event) { + final int selectedIndex = mediaPathListView.getSelectionModel().getSelectedIndex(); + Collections.swap(pad.getPaths(), selectedIndex, selectedIndex - 1); + mediaPathListView.getSelectionModel().select(selectedIndex - 1); + pad.getContent().reorderMedia(); + } + + @FXML + public void onDownAction(ActionEvent event) { + final int selectedIndex = mediaPathListView.getSelectionModel().getSelectedIndex(); + Collections.swap(pad.getPaths(), selectedIndex, selectedIndex + 1); + mediaPathListView.getSelectionModel().select(selectedIndex + 1); + pad.getContent().reorderMedia(); + } + + @FXML + public void onShowFileHandler(ActionEvent event) { + MediaPath mediaPath = mediaPathListView.getSelectionModel().getSelectedItem(); + NativeApplication.sharedInstance().showFileInFileViewer(mediaPath.getPath()); + } + + @FXML + public void onDeleteHandler(ActionEvent event) { + MediaPath mediaPath = mediaPathListView.getSelectionModel().getSelectedItem(); + pad.removePath(mediaPath); + + if (pad.getPaths().isEmpty()) { + pad.clear(); + } + } +} diff --git a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/TriggerPadTabViewController.java b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/TriggerPadTabViewController.java index 33fd3432634f564d5718ba8fc85d4fc4725a42b7..d9e1d8fc62dee9745734c99a475b77898a0bf5ea 100644 --- a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/TriggerPadTabViewController.java +++ b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/TriggerPadTabViewController.java @@ -8,6 +8,7 @@ import de.tobias.playpad.tigger.TriggerPoint; import de.tobias.playpad.trigger.TriggerDisplayable; import de.tobias.playpad.viewcontroller.PadSettingsTabViewController; import de.tobias.playpad.viewcontroller.cell.DisplayableTreeCell; +import de.tobias.playpad.viewcontroller.main.IMainViewController; import de.tobias.playpad.viewcontroller.option.pad.trigger.TriggerPointViewController; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; @@ -26,9 +27,11 @@ public class TriggerPadTabViewController extends PadSettingsTabViewController im @FXML private VBox contentView; - private Pad pad; + private final Pad pad; + private final IMainViewController mainViewController; - TriggerPadTabViewController(Pad pad) { + TriggerPadTabViewController(Pad pad, IMainViewController mainViewController) { + this.mainViewController = mainViewController; load("view/option/pad", "TriggerTab", Localization.getBundle()); this.pad = pad; } @@ -61,7 +64,7 @@ public class TriggerPadTabViewController extends PadSettingsTabViewController im if (newValue != null) { TriggerDisplayable triggerWrapper = newValue.getValue(); - TriggerPointViewController controller = new TriggerPointViewController(triggerWrapper); + TriggerPointViewController controller = new TriggerPointViewController(mainViewController, triggerWrapper); contentView.getChildren().setAll(controller.getParent()); VBox.setVgrow(controller.getParent(), Priority.ALWAYS); } diff --git a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/trigger/CartTriggerViewController.java b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/trigger/CartTriggerViewController.java index a5d2f0ea9780c898b90e1ebd98efff2c60d19653..1168ae13aedb828a12ba14ff18c14bad68718a1d 100644 --- a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/trigger/CartTriggerViewController.java +++ b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/trigger/CartTriggerViewController.java @@ -8,6 +8,7 @@ import de.tobias.playpad.pad.PadStatus; import de.tobias.playpad.project.Project; import de.tobias.playpad.trigger.CartTriggerItem; import de.tobias.playpad.view.main.ProjectPreviewView; +import de.tobias.playpad.viewcontroller.main.IMainViewController; import javafx.beans.InvalidationListener; import javafx.fxml.FXML; import javafx.geometry.Insets; @@ -25,17 +26,17 @@ public class CartTriggerViewController extends NVC { @FXML private CheckBox allCartsCheckbox; - private ProjectPreviewView projectPreviewView; + private final ProjectPreviewView projectPreviewView; - private CartTriggerItem item; + private final CartTriggerItem item; - public CartTriggerViewController(CartTriggerItem item) { + public CartTriggerViewController(CartTriggerItem item, IMainViewController mainViewController) { load("view/option/pad/trigger", "CartTrigger", Localization.getBundle()); this.item = item; Project project = PlayPadMain.getProgramInstance().getCurrentProject(); final List<Pad> pads = item.getCarts().stream().map(project::getPad).collect(Collectors.toList()); - projectPreviewView = new ProjectPreviewView(project, pads); + projectPreviewView = new ProjectPreviewView(project, pads, mainViewController.getPage()); projectPreviewView.setPadding(new Insets(0, 0, 0, 164)); projectPreviewView.selectedProperty().addListener((InvalidationListener) observable -> { item.getCarts().clear(); diff --git a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/trigger/TriggerPointViewController.java b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/trigger/TriggerPointViewController.java index 77d5166b7191a5da2d5f2565bd1221da47e830b4..85793e58329cc0793f8919085cf1059bd01cb500 100644 --- a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/trigger/TriggerPointViewController.java +++ b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/trigger/TriggerPointViewController.java @@ -10,6 +10,7 @@ import de.tobias.playpad.registry.NoSuchComponentException; import de.tobias.playpad.tigger.TriggerItem; import de.tobias.playpad.tigger.TriggerItemFactory; import de.tobias.playpad.trigger.TriggerDisplayable; +import de.tobias.playpad.viewcontroller.main.IMainViewController; import javafx.fxml.FXML; import javafx.scene.control.Button; import javafx.scene.control.ContentDisplay; @@ -26,14 +27,17 @@ public class TriggerPointViewController extends NVC { @FXML private HBox buttonBox; - private TriggerDisplayable triggerWrapper; + private final IMainViewController mainViewController; + private final TriggerDisplayable triggerWrapper; - public TriggerPointViewController(TriggerDisplayable triggerWrapper) { + public TriggerPointViewController(IMainViewController mainViewController, TriggerDisplayable triggerWrapper) { + this.mainViewController = mainViewController; load("view/option/pad/trigger", "TriggerPoint", Localization.getBundle()); this.triggerWrapper = triggerWrapper; - for (TriggerItem item : triggerWrapper.getTrigger().getItems()) - showTriggerItem(item); + for (TriggerItem item : triggerWrapper.getTrigger().getItems()) { + showTriggerItem(item, mainViewController); + } } @Override @@ -42,17 +46,17 @@ public class TriggerPointViewController extends NVC { types.stream().sorted().forEach(item -> { try { - TriggerItemFactory conntect = PlayPadPlugin.getRegistries().getTriggerItems().getFactory(item); - Button button = new Button(conntect.toString(), new FontIcon(FontAwesomeType.PLUS_CIRCLE)); + TriggerItemFactory factory = PlayPadPlugin.getRegistries().getTriggerItems().getFactory(item); + Button button = new Button(factory.toString(), new FontIcon(FontAwesomeType.PLUS_CIRCLE)); button.setContentDisplay(ContentDisplay.TOP); button.setPrefWidth(150); button.setOnAction(e -> { - TriggerItem triggerItem = conntect.newInstance(triggerWrapper.getTrigger()); + TriggerItem triggerItem = factory.newInstance(triggerWrapper.getTrigger()); triggerWrapper.addItem(triggerItem); - showTriggerItem(triggerItem); + showTriggerItem(triggerItem, mainViewController); }); buttonBox.getChildren().add(button); } catch (NoSuchComponentException e) { @@ -61,17 +65,19 @@ public class TriggerPointViewController extends NVC { }); } - private void showTriggerItem(TriggerItem item) { + private void showTriggerItem(TriggerItem item, IMainViewController mainViewController) { try { TriggerItemFactory connect = PlayPadPlugin.getRegistries().getTriggerItems().getFactory(item.getType()); VBox itemBox = new VBox(14); - NVC controller = connect.getSettingsController(item); + NVC controller = connect.getSettingsController(item, mainViewController); if (controller != null) { itemBox.getChildren().add(controller.getParent()); - NVC timeViewController = new TriggerTimeViewController(item); - itemBox.getChildren().add(timeViewController.getParent()); + if (triggerWrapper.getTrigger().getTriggerPoint().isTimeAppendable()) { + final NVC timeViewController = new TriggerTimeViewController(item); + itemBox.getChildren().add(timeViewController.getParent()); + } Button deleteButton = new Button("", new FontIcon(FontAwesomeType.TRASH)); HBox hbox = new HBox(itemBox, deleteButton); diff --git a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/trigger/VolumeTriggerViewController.java b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/trigger/VolumeTriggerViewController.java index 10a1a70dfb79ad38c6eedcacaa450384a65cdef9..f48a57341e8a354af30dafc4ead00f256d7955f4 100644 --- a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/trigger/VolumeTriggerViewController.java +++ b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/trigger/VolumeTriggerViewController.java @@ -8,6 +8,7 @@ import de.tobias.playpad.pad.Pad; import de.tobias.playpad.project.Project; import de.tobias.playpad.trigger.VolumeTriggerItem; import de.tobias.playpad.view.main.ProjectPreviewView; +import de.tobias.playpad.viewcontroller.main.IMainViewController; import javafx.beans.InvalidationListener; import javafx.fxml.FXML; import javafx.geometry.Insets; @@ -30,20 +31,20 @@ public class VolumeTriggerViewController extends NVC { private Slider durationSlider; @FXML private Label durationLabel; - private ProjectPreviewView projectPreviewView; + private final ProjectPreviewView projectPreviewView; - private VolumeTriggerItem item; + private final VolumeTriggerItem item; - public VolumeTriggerViewController(VolumeTriggerItem item) { + public VolumeTriggerViewController(VolumeTriggerItem item, IMainViewController mainViewController) { load("view/option/pad/trigger", "VolumeTrigger", Localization.getBundle()); this.item = item; volumeSlider.setValue(item.getVolume() * 100.0); durationSlider.setValue(item.getDuration().toSeconds()); - Project project = PlayPadMain.getProgramInstance().getCurrentProject(); + final Project project = PlayPadMain.getProgramInstance().getCurrentProject(); final List<Pad> pads = item.getCarts().stream().map(project::getPad).collect(Collectors.toList()); - projectPreviewView = new ProjectPreviewView(project, pads); + projectPreviewView = new ProjectPreviewView(project, pads, mainViewController.getPage()); projectPreviewView.setPadding(new Insets(0, 0, 0, 164)); projectPreviewView.selectedProperty().addListener((InvalidationListener) observable -> { item.getCarts().clear(); diff --git a/PlayWall/src/main/resources/lang/_de.properties b/PlayWall/src/main/resources/lang/_de.properties index 5659469960a8fb5226e22da2c1d3e406d41a382c..33029a528b99644493e53bcd9b83e304ae7fd03d 100755 --- a/PlayWall/src/main/resources/lang/_de.properties +++ b/PlayWall/src/main/resources/lang/_de.properties @@ -158,6 +158,7 @@ Info.Mapper.PressKey=Dr\u00FCcken Sie eine Taste auf dem Ger\u00E4t. UI.Settings.Alert.NewKeyShortcut.Text=Dr\u00FCcken Sie bitte die gew\u00FCnschte Tastenkombination. \nDieses kann aus Buchstaben, Ziffern und/oder den F-Tasten bestehen. # UI - Window - PadSettings UI.Window.PadSettings.General.Title=Allgemein +UI.Window.PadSettings.Playlist.Title=Playlist UI.Window.PadSettings.Player.Title=Player UI.Window.PadSettings.Layout.Title=Layout UI.Window.PadSettings.Trigger.Title=Trigger @@ -208,14 +209,18 @@ UI.Window.Settings.Updates.CurrentVersion={} (Build {}) TriggerPoint.toString={} ({}) Trigger.Cart.Name=Kacheln Trigger.Volume.Name=Lautst\u00E4rke + # TriggerPoint - Enum TriggerPoint.START=Start TriggerPoint.STOP=Stop -TriggerPoint.EOF=Ende (EoF) +TriggerPoint.EOF=Ende +TriggerPoint.EOF_STATE=Ende (EoF) + # Drag and Drop Mode DnDMode.Replace=Ersetzen DnDMode.Duplicate=Duplizieren DnDMode.Move=Tauschen +DndMode.Playlist=Hinzuf\u00FCgen # Main Layout MainLayout.Desktop=Desktopmodus MainLayout.Touch=Touchmodus diff --git a/PlayWall/src/main/resources/view/option/pad/GeneralTab.fxml b/PlayWall/src/main/resources/view/option/pad/GeneralTab.fxml index 897ebd58dca41a69254bfe6b885e6d55cbc1a3fd..918175e2602fd5ef7166f7392ed40efcbc8838ca 100644 --- a/PlayWall/src/main/resources/view/option/pad/GeneralTab.fxml +++ b/PlayWall/src/main/resources/view/option/pad/GeneralTab.fxml @@ -3,27 +3,33 @@ <?import javafx.geometry.Insets?> <?import javafx.scene.control.*?> <?import javafx.scene.layout.*?> -<VBox spacing="14.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1"> +<VBox spacing="14.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"> <children> - <HBox alignment="CENTER_LEFT" spacing="14.0"> + <VBox fx:id="mediaRootBox" spacing="14.0"> <children> - <Label alignment="CENTER_RIGHT" prefWidth="150.0" text="%padSettings.gen.label.media"/> - <Label fx:id="pathLabel" text="Label" textOverrun="CENTER_ELLIPSIS"/> - <VBox HBox.hgrow="ALWAYS"/> - <Button fx:id="showPathButton" mnemonicParsing="false" onAction="#showPathButtonHandler" text="%padSettings.button.path.show"/> + <HBox alignment="CENTER_LEFT" spacing="14.0"> + <children> + <Label alignment="CENTER_RIGHT" prefWidth="150.0" text="%padSettings.gen.label.media"/> + <Label fx:id="pathLabel" text="Label" textOverrun="CENTER_ELLIPSIS"/> + <VBox HBox.hgrow="ALWAYS"/> + <Button fx:id="showPathButton" mnemonicParsing="false" onAction="#showPathButtonHandler" + text="%padSettings.button.path.show"/> + </children> + </HBox> + <HBox spacing="14.0"> + <children> + <Button mnemonicParsing="false" onAction="#chooseButtonHandler" + text="%padSettings.button.path.choose"/> + <Button fx:id="deleteButton" mnemonicParsing="false" onAction="#deleteButtonHandler" + text="%padSettings.button.delete"/> + </children> + <padding> + <Insets left="164.0"/> + </padding> + </HBox> + <Separator prefWidth="200.0"/> </children> - </HBox> - <HBox spacing="14.0"> - <children> - <Button mnemonicParsing="false" onAction="#chooseButtonHandler" text="%padSettings.button.path.choose"/> - <Button fx:id="deleteButton" mnemonicParsing="false" onAction="#deleteButtonHandler" - text="%padSettings.button.delete"/> - </children> - <padding> - <Insets left="164.0"/> - </padding> - </HBox> - <Separator prefWidth="200.0"/> + </VBox> <HBox alignment="CENTER_LEFT" spacing="14.0"> <children> <Label alignment="CENTER_RIGHT" layoutX="14.0" layoutY="19.0" prefWidth="150.0" diff --git a/PlayWall/src/main/resources/view/option/pad/PlaylistTab.fxml b/PlayWall/src/main/resources/view/option/pad/PlaylistTab.fxml new file mode 100644 index 0000000000000000000000000000000000000000..6f25be3e5792c953ce621d4ae05e0ca0db5b8c8f --- /dev/null +++ b/PlayWall/src/main/resources/view/option/pad/PlaylistTab.fxml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<?import javafx.geometry.Insets?> +<?import javafx.scene.control.*?> +<?import javafx.scene.layout.*?> +<HBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" spacing="14.0" + xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"> + <children> + <VBox spacing="14.0"> + <children> + <ListView fx:id="mediaPathListView" prefWidth="250.0" VBox.vgrow="ALWAYS"/> + <HBox spacing="14.0"> + <children> + <Button fx:id="addButton" maxWidth="1.7976931348623157E308" mnemonicParsing="false" + onAction="#onAddHandler" HBox.hgrow="ALWAYS"/> + <Button fx:id="upButton" maxWidth="1.7976931348623157E308" mnemonicParsing="false" + onAction="#onUpHandler" HBox.hgrow="ALWAYS"/> + <Button fx:id="downButton" maxWidth="1.7976931348623157E308" mnemonicParsing="false" + onAction="#onDownAction" HBox.hgrow="ALWAYS"/> + </children> + </HBox> + </children> + </VBox> + <VBox spacing="14.0"> + <children> + <HBox spacing="14.0"> + <children> + <Label fx:id="pathLabel" textOverrun="CENTER_ELLIPSIS"/> + </children> + </HBox> + <HBox spacing="14.0"> + <children> + <Button fx:id="showFileButton" mnemonicParsing="false" onAction="#onShowFileHandler" + text="%padSettings.button.path.show"/> + <Button fx:id="deleteButton" mnemonicParsing="false" onAction="#onDeleteHandler" + text="%padSettings.button.delete"/> + </children> + </HBox> + </children> + <HBox.margin> + <Insets/> + </HBox.margin> + <padding> + <Insets bottom="14.0" left="14.0" right="14.0" top="14.0"/> + </padding> + </VBox> + </children> + <padding> + <Insets bottom="14.0" left="14.0" right="14.0" top="14.0"/> + </padding> +</HBox> diff --git a/PlayWall/src/main/sass/components/dark-list.scss b/PlayWall/src/main/sass/components/dark-list.scss new file mode 100644 index 0000000000000000000000000000000000000000..080f181297955b8f85c6922e539e69705d001645 --- /dev/null +++ b/PlayWall/src/main/sass/components/dark-list.scss @@ -0,0 +1,23 @@ +@import "variables"; + +.dark-list { + -fx-base: rgb(60, 60, 60); + + + .list-cell { + -fx-skin: "com.sun.javafx.scene.control.skin.ListCellSkin"; + -fx-background-color: -fx-base; + -fx-padding: 10px; + -fx-text-fill: #FFFFFF; + -fx-opacity: 1; + + &:filled { + -fx-border-width: 0px 0px 1px 0px; + -fx-border-color: #00000044; + } + + &:odd { + -fx-background-color: -fx-base; + } + } +} \ No newline at end of file diff --git a/PlayWall/src/main/sass/components/list.scss b/PlayWall/src/main/sass/components/list.scss index 1ea1f4bc53dc52ac2bfb04568b503e2d64412451..45bcf857eb7c9f26a7ae5cf1a91c174cf92f75df 100644 --- a/PlayWall/src/main/sass/components/list.scss +++ b/PlayWall/src/main/sass/components/list.scss @@ -1,19 +1,22 @@ @import "variables"; -.dark-list { +.list-view { -fx-base: rgb(60, 60, 60); .list-cell { -fx-skin: "com.sun.javafx.scene.control.skin.ListCellSkin"; -fx-background-color: -fx-base; - -fx-padding: 10px; + -fx-padding: 4px; -fx-text-fill: #FFFFFF; -fx-opacity: 1; &:filled { -fx-border-width: 0px 0px 1px 0px; - -fx-border-color: #00000044; + } + + &:odd:filled { + -fx-background-color: derive(-fx-hover-base, -10%); } } diff --git a/PlayWall/src/main/sass/modern.scss b/PlayWall/src/main/sass/modern.scss index 7046c6af039f7e778946349465614c6b1a8e9f6b..bb353f6186cc4e1be629a85988ceb1745bdb2908 100644 --- a/PlayWall/src/main/sass/modern.scss +++ b/PlayWall/src/main/sass/modern.scss @@ -12,6 +12,7 @@ @import "components/menu"; @import "components/toggle-button"; @import "components/list"; +@import "components/dark-list"; @import "components/progress-bar"; @import "components/dialog"; @import "components/treeview"; diff --git a/PlayWall/src/main/scala/de/tobias/playpad/design/ModernCartDesignHandlerImpl.scala b/PlayWall/src/main/scala/de/tobias/playpad/design/ModernCartDesignHandlerImpl.scala index aec4ccf9145de83e3b41e86038504f1dbf585ea9..b639f9099419bd956a0b420216f9c01568b2c679 100644 --- a/PlayWall/src/main/scala/de/tobias/playpad/design/ModernCartDesignHandlerImpl.scala +++ b/PlayWall/src/main/scala/de/tobias/playpad/design/ModernCartDesignHandlerImpl.scala @@ -4,7 +4,7 @@ import de.thecodelabs.utils.application.ApplicationUtils import de.tobias.playpad.design.modern.model.{ModernCartDesign, ModernGlobalDesign} import de.tobias.playpad.design.modern.{ModernCartDesignHandler, ModernColor} import de.tobias.playpad.pad.content.play.Durationable -import de.tobias.playpad.pad.viewcontroller.IPadViewController +import de.tobias.playpad.pad.viewcontroller.AbstractPadViewController import de.tobias.playpad.util.Minifier import de.tobias.playpad.view.PseudoClasses import javafx.util.Duration @@ -47,7 +47,7 @@ class ModernCartDesignHandlerImpl extends ModernCartDesignHandler { expressionParser.parseExpression(string, new TemplateParserContext("${", "}")).getValue(context, classOf[String]) } - override def handleWarning(design: ModernCartDesign, controller: IPadViewController, warning: Duration, globalDesign: ModernGlobalDesign): Unit = { + override def handleWarning(design: ModernCartDesign, controller: AbstractPadViewController, warning: Duration, globalDesign: ModernGlobalDesign): Unit = { if (globalDesign.isWarnAnimation) { val playColor = design.getPlayColor val backgroundColor = design.getBackgroundColor @@ -71,5 +71,5 @@ class ModernCartDesignHandlerImpl extends ModernCartDesignHandler { } } - override def stopWarning(design: ModernCartDesign, controller: IPadViewController): Unit = ModernDesignAnimator.stopAnimation(controller) + override def stopWarning(design: ModernCartDesign, controller: AbstractPadViewController): Unit = ModernDesignAnimator.stopAnimation(controller) } diff --git a/PlayWall/src/main/scala/de/tobias/playpad/design/ModernGlobalDesignHandlerImpl.scala b/PlayWall/src/main/scala/de/tobias/playpad/design/ModernGlobalDesignHandlerImpl.scala index ca1ade7277a8ba8e726e3d3b1844575ea2b47f0b..3eea45b74878a931b801b72ea341ce13375be7d2 100644 --- a/PlayWall/src/main/scala/de/tobias/playpad/design/ModernGlobalDesignHandlerImpl.scala +++ b/PlayWall/src/main/scala/de/tobias/playpad/design/ModernGlobalDesignHandlerImpl.scala @@ -1,14 +1,11 @@ package de.tobias.playpad.design -import java.nio.file.Files -import java.util.function.Consumer - import de.thecodelabs.utils.application.ApplicationUtils import de.thecodelabs.utils.application.container.PathType import de.tobias.playpad.design.modern.model.{ModernCartDesign, ModernGlobalDesign} import de.tobias.playpad.design.modern.{ModernColor, ModernGlobalDesignHandler} import de.tobias.playpad.pad.content.play.Durationable -import de.tobias.playpad.pad.viewcontroller.IPadViewController +import de.tobias.playpad.pad.viewcontroller.AbstractPadViewController import de.tobias.playpad.project.Project import de.tobias.playpad.util.Minifier import de.tobias.playpad.view.{ColorPickerView, PseudoClasses} @@ -22,6 +19,8 @@ import org.springframework.expression.common.TemplateParserContext import org.springframework.expression.spel.standard.SpelExpressionParser import org.springframework.expression.spel.support.StandardEvaluationContext +import java.nio.file.Files +import java.util.function.Consumer import scala.jdk.CollectionConverters._ class ModernGlobalDesignHandlerImpl extends ModernGlobalDesignHandler with ColorModeHandler { @@ -93,7 +92,7 @@ class ModernGlobalDesignHandlerImpl extends ModernGlobalDesignHandler with Color expressionParser.parseExpression(string, new TemplateParserContext("${", "}")).getValue(context, classOf[String]) } - override def handleWarning(design: ModernGlobalDesign, controller: IPadViewController, warning: Duration): Unit = { + override def handleWarning(design: ModernGlobalDesign, controller: AbstractPadViewController, warning: Duration): Unit = { if (design.isWarnAnimation) { warnAnimation(design, controller, warning) } else { @@ -101,7 +100,7 @@ class ModernGlobalDesignHandlerImpl extends ModernGlobalDesignHandler with Color } } - private def warnAnimation(design: ModernGlobalDesign, controller: IPadViewController, warning: Duration): Unit = { + private def warnAnimation(design: ModernGlobalDesign, controller: AbstractPadViewController, warning: Duration): Unit = { val stopColor = if (design.isFlatDesign) design.getBackgroundColor.toFlatFadeableColor else design.getBackgroundColor.toFadeableColor val playColor = if (design.isFlatDesign) design.getPlayColor.toFlatFadeableColor else design.getPlayColor.toFadeableColor @@ -118,7 +117,7 @@ class ModernGlobalDesignHandlerImpl extends ModernGlobalDesignHandler with Color ModernDesignAnimator.animateWarn(controller, playColor, stopColor, duration) } - override def stopWarning(design: ModernGlobalDesign, controller: IPadViewController): Unit = ModernDesignAnimator.stopAnimation(controller) + override def stopWarning(design: ModernGlobalDesign, controller: AbstractPadViewController): Unit = ModernDesignAnimator.stopAnimation(controller) override def getColorInterface(onSelection: Consumer[DisplayableColor]) = new ColorPickerView(null, ModernColor.values.asInstanceOf[Array[DisplayableColor]], onSelection) diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/design/modern/ModernCartDesignHandler.java b/PlayWallCore/src/main/java/de/tobias/playpad/design/modern/ModernCartDesignHandler.java index 0f34344711461d7ef8a26a175053fc2be731f27b..200ead1fd6e412137370b6393167bb1092adc90d 100644 --- a/PlayWallCore/src/main/java/de/tobias/playpad/design/modern/ModernCartDesignHandler.java +++ b/PlayWallCore/src/main/java/de/tobias/playpad/design/modern/ModernCartDesignHandler.java @@ -2,7 +2,7 @@ package de.tobias.playpad.design.modern; import de.tobias.playpad.design.modern.model.ModernCartDesign; import de.tobias.playpad.design.modern.model.ModernGlobalDesign; -import de.tobias.playpad.pad.viewcontroller.IPadViewController; +import de.tobias.playpad.pad.viewcontroller.AbstractPadViewController; import javafx.util.Duration; public interface ModernCartDesignHandler { @@ -12,9 +12,9 @@ public interface ModernCartDesignHandler { /* * Wird in einem neuen Thread aufgerufen */ - void handleWarning(ModernCartDesign design, IPadViewController controller, Duration warning, ModernGlobalDesign globalDesign); + void handleWarning(ModernCartDesign design, AbstractPadViewController controller, Duration warning, ModernGlobalDesign globalDesign); - default void stopWarning(ModernCartDesign design, IPadViewController controller) { + default void stopWarning(ModernCartDesign design, AbstractPadViewController controller) { } } diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/design/modern/ModernGlobalDesignHandler.java b/PlayWallCore/src/main/java/de/tobias/playpad/design/modern/ModernGlobalDesignHandler.java index 1c6930dd44d0737bfce2454d18f96ba131652aff..bca1e4aa9cee672852326f41dffe582e115f10e4 100644 --- a/PlayWallCore/src/main/java/de/tobias/playpad/design/modern/ModernGlobalDesignHandler.java +++ b/PlayWallCore/src/main/java/de/tobias/playpad/design/modern/ModernGlobalDesignHandler.java @@ -1,7 +1,7 @@ package de.tobias.playpad.design.modern; import de.tobias.playpad.design.modern.model.ModernGlobalDesign; -import de.tobias.playpad.pad.viewcontroller.IPadViewController; +import de.tobias.playpad.pad.viewcontroller.AbstractPadViewController; import de.tobias.playpad.project.Project; import de.tobias.playpad.viewcontroller.main.IMainViewController; import javafx.stage.Stage; @@ -16,8 +16,8 @@ public interface ModernGlobalDesignHandler { /* * Wird in einem neuen Thread aufgerufen */ - void handleWarning(ModernGlobalDesign design, IPadViewController controller, Duration warning); + void handleWarning(ModernGlobalDesign design, AbstractPadViewController controller, Duration warning); - default void stopWarning(ModernGlobalDesign design, IPadViewController controller) { + default void stopWarning(ModernGlobalDesign design, AbstractPadViewController controller) { } } diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/pad/Pad.java b/PlayWallCore/src/main/java/de/tobias/playpad/pad/Pad.java index 2a0fd4c28cda491ab99634c36b4f2ac2ac967598..cbefe4d2a0c9c95ce5607e7bcd510b7311212ea9 100644 --- a/PlayWallCore/src/main/java/de/tobias/playpad/pad/Pad.java +++ b/PlayWallCore/src/main/java/de/tobias/playpad/pad/Pad.java @@ -13,7 +13,7 @@ import de.tobias.playpad.pad.listener.trigger.PadTriggerContentListener; import de.tobias.playpad.pad.listener.trigger.PadTriggerDurationListener; import de.tobias.playpad.pad.listener.trigger.PadTriggerStatusListener; import de.tobias.playpad.pad.mediapath.MediaPath; -import de.tobias.playpad.pad.viewcontroller.IPadViewController; +import de.tobias.playpad.pad.viewcontroller.AbstractPadViewController; import de.tobias.playpad.project.Project; import de.tobias.playpad.project.ProjectSettings; import de.tobias.playpad.project.page.PadIndex; @@ -78,7 +78,7 @@ public class Pad { // Utils private transient boolean eof; - private transient IPadViewController controller; + private transient AbstractPadViewController controller; private transient Project project; private transient PadUpdateListener padListener; @@ -323,6 +323,10 @@ public class Pad { createMediaPath(path); } else { setPath(path, 0); + + while (mediaPaths.size() > 1) { + mediaPaths.remove(mediaPaths.size() - 1); + } } } @@ -350,6 +354,13 @@ public class Pad { addPath(mediaPath); } + public void addPath(Path path) { + if (mediaPaths.isEmpty()) { + setName(PathUtils.getFilenameWithoutExtension(path.getFileName())); + } + createMediaPath(path); + } + public void addPath(MediaPath mediaPath) { mediaPaths.add(mediaPath); @@ -364,6 +375,7 @@ public class Pad { } public void removePath(MediaPath path) { + getContent().unloadMedia(path); mediaPaths.remove(path); } @@ -374,6 +386,12 @@ public class Pad { } } + public void clearPaths() { + while (!mediaPaths.isEmpty()) { + removePath(mediaPaths.get(0)); + } + } + /** * Get the status of the pad. * @@ -556,11 +574,11 @@ public class Pad { return controller != null; } - public IPadViewController getController() { + public AbstractPadViewController getController() { return controller; } - public void setController(IPadViewController controller) { + public void setController(AbstractPadViewController controller) { this.controller = controller; } diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/pad/content/PadContent.java b/PlayWallCore/src/main/java/de/tobias/playpad/pad/content/PadContent.java index bf50a0b447c04de9dd77eadb9a9ee6834ca224bc..6c6d943e9f88741e9891794bf5a68fa3d712a7df 100644 --- a/PlayWallCore/src/main/java/de/tobias/playpad/pad/content/PadContent.java +++ b/PlayWallCore/src/main/java/de/tobias/playpad/pad/content/PadContent.java @@ -40,6 +40,10 @@ public abstract class PadContent { public abstract boolean isPadLoaded(); + public boolean isPadLoading() { + return false; + } + /** * Load media files. */ @@ -64,6 +68,9 @@ public abstract class PadContent { */ public abstract void unloadMedia(MediaPath mediaPath); + public void reorderMedia() { + } + public abstract void updateVolume(); @Override diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/pad/content/PadContentFactory.java b/PlayWallCore/src/main/java/de/tobias/playpad/pad/content/PadContentFactory.java index e300c5a8b8f5f2c5a2c711bbce42629a541d51ef..611654dc50e21a013911b95a49e9024936c9b14b 100644 --- a/PlayWallCore/src/main/java/de/tobias/playpad/pad/content/PadContentFactory.java +++ b/PlayWallCore/src/main/java/de/tobias/playpad/pad/content/PadContentFactory.java @@ -2,20 +2,23 @@ package de.tobias.playpad.pad.content; import de.thecodelabs.utils.io.PathUtils; import de.tobias.playpad.pad.Pad; +import de.tobias.playpad.pad.drag.ContentDragOption; import de.tobias.playpad.pad.view.IPadContentView; import de.tobias.playpad.registry.Component; import de.tobias.playpad.viewcontroller.PadSettingsTabViewController; import de.tobias.playpad.viewcontroller.option.ProfileSettingsTabViewController; import javafx.scene.layout.Pane; +import java.io.File; import java.nio.file.Path; -import java.util.Set; +import java.util.Collection; +import java.util.List; import java.util.function.Consumer; -public abstract class PadContentFactory extends Component implements Comparable<PadContentFactory> { +public abstract class PadContentFactory extends Component implements ContentDragOption { public interface PadContentTypeChooser { - void showOptions(Set<PadContentFactory> options, Consumer<PadContentFactory> onSelected); + void showOptions(Collection<PadContentFactory> options, Consumer<PadContentFactory> onSelected); } public PadContentFactory(String type) { @@ -36,11 +39,6 @@ public abstract class PadContentFactory extends Component implements Comparable< public abstract String[] getSupportedTypes(); - @Override - public int compareTo(PadContentFactory o) { - return getType().compareTo(o.getType()); - } - public static boolean isFileTypeSupported(Path path, PadContentFactory connect) { String extension = PathUtils.getFileExtension(path); for (String ex : connect.getSupportedTypes()) { @@ -50,4 +48,26 @@ public abstract class PadContentFactory extends Component implements Comparable< } return false; } + + // Generic Drag Option for all content types + + @Override + public void handleDrop(Pad currentPad, List<File> files) { + if (currentPad.getContent() == null || !currentPad.getContent().getType().equals(getType())) { + currentPad.setContentType(getType()); + } + + if (currentPad.isPadVisible()) { + currentPad.getController().getView().showBusyView(true); + } + + if (currentPad.getContent() instanceof Playlistable) { + currentPad.clearPaths(); + for (File file : files) { + currentPad.addPath(file.toPath()); + } + } else { + currentPad.setPath(files.get(0).toPath()); + } + } } diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/pad/content/PadContentRegistry.java b/PlayWallCore/src/main/java/de/tobias/playpad/pad/content/PadContentRegistry.java index 20e7f45d2ef3f798555acda8d0062c9cad47c5f5..a2614a95f34e7f86294ff57beb87777c818e8bcb 100644 --- a/PlayWallCore/src/main/java/de/tobias/playpad/pad/content/PadContentRegistry.java +++ b/PlayWallCore/src/main/java/de/tobias/playpad/pad/content/PadContentRegistry.java @@ -12,17 +12,30 @@ public class PadContentRegistry extends ComponentRegistry<PadContentFactory> { super(name); } - public Set<PadContentFactory> getPadContentConnectsForFile(Path path) throws NoSuchComponentException { - Set<PadContentFactory> connects = new HashSet<>(); + public List<PadContentFactory> getPadContentConnectsForFile(Path paths) throws NoSuchComponentException { + return getPadContentConnectsForFiles(Collections.singletonList(paths)); + } + + public List<PadContentFactory> getPadContentConnectsForFiles(List<Path> paths) throws NoSuchComponentException { + final Set<PadContentFactory> connects = new HashSet<>(); for (String type : getTypes()) { PadContentFactory connect = getFactory(type); for (String extension : connect.getSupportedTypes()) { - if (path.getFileName().toString().toLowerCase().matches("." + extension)) { + if (isExtensionMatchingAllFiles(extension, paths)) { connects.add(connect); } } } - return connects; + return new ArrayList<>(connects); + } + + private boolean isExtensionMatchingAllFiles(String extension, List<Path> paths) { + for (Path path : paths) { + if (!path.getFileName().toString().toLowerCase().matches("." + extension)) { + return false; + } + } + return true; } public String[] getSupportedFileTypes() throws NoSuchComponentException { diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/pad/content/Playlistable.java b/PlayWallCore/src/main/java/de/tobias/playpad/pad/content/Playlistable.java new file mode 100644 index 0000000000000000000000000000000000000000..1d15d9ad02184a18dc076b6cce7d739f8eba9552 --- /dev/null +++ b/PlayWallCore/src/main/java/de/tobias/playpad/pad/content/Playlistable.java @@ -0,0 +1,16 @@ +package de.tobias.playpad.pad.content; + +import de.tobias.playpad.pad.mediapath.MediaPath; +import javafx.beans.property.IntegerProperty; + +public interface Playlistable { + int getCurrentPlayingMediaIndex(); + + IntegerProperty currentPlayingMediaIndexProperty(); + + boolean hasNext(); + + void next(); + + boolean isLoaded(MediaPath mediaPath); +} diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/pad/drag/ContentDragOption.java b/PlayWallCore/src/main/java/de/tobias/playpad/pad/drag/ContentDragOption.java new file mode 100644 index 0000000000000000000000000000000000000000..8af2e6d82227376781157fc2f9bd46b9073ce53f --- /dev/null +++ b/PlayWallCore/src/main/java/de/tobias/playpad/pad/drag/ContentDragOption.java @@ -0,0 +1,17 @@ +package de.tobias.playpad.pad.drag; + +import de.tobias.playpad.Displayable; +import de.tobias.playpad.pad.Pad; + +import java.io.File; +import java.util.List; + +public interface ContentDragOption extends Displayable, Comparable<ContentDragOption> { + + void handleDrop(Pad currentPad, List<File> files); + + @Override + default int compareTo(ContentDragOption o) { + return displayProperty().get().compareTo(o.displayProperty().get()); + } +} diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/pad/fade/LinearFadeController.java b/PlayWallCore/src/main/java/de/tobias/playpad/pad/fade/LinearFadeController.java new file mode 100644 index 0000000000000000000000000000000000000000..3287ca97157df9df9aac614b43ccd21baa49e94a --- /dev/null +++ b/PlayWallCore/src/main/java/de/tobias/playpad/pad/fade/LinearFadeController.java @@ -0,0 +1,26 @@ +package de.tobias.playpad.pad.fade; + +import javafx.animation.Transition; + +/** + * A fade controller implementation, that handles fade scala linear. + * + * @author tobias + * @since 7.1.0 + */ +public class LinearFadeController extends AbstractFadeController { + + public LinearFadeController(FadeControllerDelegate fadeDelegate) { + super(fadeDelegate); + } + + @Override + protected void interpolate(Transition transition, double frac, double from, double to) { + double diff = Math.abs(to - from); + if (from < to) { // Fade In + fadeDelegate.onFadeLevelChange(diff * frac); + } else { // Fade Out + fadeDelegate.onFadeLevelChange(from - (diff * frac)); + } + } +} diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/pad/fade/listener/PadFadeContentListener.java b/PlayWallCore/src/main/java/de/tobias/playpad/pad/fade/listener/PadFadeContentListener.java index ad414c8d8250053b5a3ec2f279c3b3bfd676e57c..0d7436b3405d2f89be63feebeaf36f55fe213889 100644 --- a/PlayWallCore/src/main/java/de/tobias/playpad/pad/fade/listener/PadFadeContentListener.java +++ b/PlayWallCore/src/main/java/de/tobias/playpad/pad/fade/listener/PadFadeContentListener.java @@ -7,9 +7,12 @@ import de.tobias.playpad.pad.fade.Fadeable; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; +/** + * Remove end of file fade listener on old pad content and register on new pad content + */ public class PadFadeContentListener implements ChangeListener<PadContent> { - private Pad pad; + private final Pad pad; public PadFadeContentListener(Pad pad) { this.pad = pad; diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/pad/fade/listener/PadFadeDurationListener.java b/PlayWallCore/src/main/java/de/tobias/playpad/pad/fade/listener/PadFadeDurationListener.java index b795033f65d4b4004432d8c53d82a8ee0f115939..214f832d81b61a8449170e5443a83a1fa32c57de 100644 --- a/PlayWallCore/src/main/java/de/tobias/playpad/pad/fade/listener/PadFadeDurationListener.java +++ b/PlayWallCore/src/main/java/de/tobias/playpad/pad/fade/listener/PadFadeDurationListener.java @@ -1,6 +1,7 @@ package de.tobias.playpad.pad.fade.listener; import de.tobias.playpad.pad.Pad; +import de.tobias.playpad.pad.content.Playlistable; import de.tobias.playpad.pad.content.play.Durationable; import de.tobias.playpad.pad.fade.Fadeable; import javafx.beans.value.ChangeListener; @@ -20,6 +21,16 @@ public class PadFadeDurationListener implements ChangeListener<Duration> { if (pad.getPadSettings().getFade().isFadeOutEof()) { final Duration fadeDuration = pad.getPadSettings().getFade().getFadeOut(); + // Do not fade out if looping is enabled + if (pad.getPadSettings().isLoop()) { + return; + } + + // Do not fade out if the playlist has a next entry + if (pad.getContent() instanceof Playlistable && ((Playlistable) pad.getContent()).hasNext()) { + return; + } + if (pad.getContent() instanceof Durationable) { final Durationable durationable = (Durationable) pad.getContent(); diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/pad/listener/trigger/PadTriggerDurationListener.java b/PlayWallCore/src/main/java/de/tobias/playpad/pad/listener/trigger/PadTriggerDurationListener.java index 5ed71ddb3703d706c5591e14ded194d1481df576..834121676c93bc610e53b4ed255d8c06536991ae 100644 --- a/PlayWallCore/src/main/java/de/tobias/playpad/pad/listener/trigger/PadTriggerDurationListener.java +++ b/PlayWallCore/src/main/java/de/tobias/playpad/pad/listener/trigger/PadTriggerDurationListener.java @@ -22,22 +22,22 @@ public class PadTriggerDurationListener implements ChangeListener<Duration> { } @Override - public void changed(ObservableValue<? extends Duration> observable, Duration oldValue, Duration newValue) { + public void changed(ObservableValue<? extends Duration> observable, Duration oldValue, Duration currentTime) { PadContent content = pad.getContent(); if (content instanceof Durationable) { Duration totalDuration = ((Durationable) content).getDuration(); if (totalDuration != null) { - Duration leftTime = totalDuration.subtract(newValue); - IMainViewController mainViewController = PlayPadPlugin.getInstance().getMainViewController(); Profile currentProfile = Profile.currentProfile(); PadSettings padSettings = pad.getPadSettings(); - // Execute Triggers - Trigger startTrigger = padSettings.getTrigger(TriggerPoint.START); - startTrigger.handle(pad, newValue, pad.getProject(), mainViewController, currentProfile); + // Execute Start Triggers + final Trigger startTrigger = padSettings.getTrigger(TriggerPoint.START); + startTrigger.handle(pad, currentTime, pad.getProject(), mainViewController, currentProfile); - Trigger endTrigger = padSettings.getTrigger(TriggerPoint.EOF); + // Execute End Trigger + final Duration leftTime = totalDuration.subtract(currentTime); + final Trigger endTrigger = padSettings.getTrigger(TriggerPoint.EOF); endTrigger.handle(pad, leftTime, pad.getProject(), mainViewController, currentProfile); } } diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/pad/listener/trigger/PadTriggerStatusListener.java b/PlayWallCore/src/main/java/de/tobias/playpad/pad/listener/trigger/PadTriggerStatusListener.java index f7edfbdf7c483255dc437116d980f14832e59d42..94459f05e67e68795cc885c73be904a798fced97 100644 --- a/PlayWallCore/src/main/java/de/tobias/playpad/pad/listener/trigger/PadTriggerStatusListener.java +++ b/PlayWallCore/src/main/java/de/tobias/playpad/pad/listener/trigger/PadTriggerStatusListener.java @@ -26,12 +26,12 @@ public class PadTriggerStatusListener implements ChangeListener<PadStatus> { PadSettings padSettings = pad.getPadSettings(); // Execute Trigger - if (newValue == PadStatus.PLAY) { // TRIGGER FÜR START + if (newValue == PadStatus.PLAY) { executeTrigger(padSettings.getTriggers().get(TriggerPoint.START)); - } else if (newValue == PadStatus.STOP) { // TRIGGER FÜR STOP + } else if (newValue == PadStatus.STOP) { executeTrigger(padSettings.getTriggers().get(TriggerPoint.STOP)); - } else if (oldValue == PadStatus.PLAY && newValue == PadStatus.READY && pad.isEof()) { // TRIGGER FÜR EOF - executeTrigger(padSettings.getTriggers().get(TriggerPoint.EOF)); + } else if (oldValue == PadStatus.PLAY && newValue == PadStatus.READY && pad.isEof()) { + executeTrigger(padSettings.getTriggers().get(TriggerPoint.EOF_STATE)); } } else { pad.setIgnoreTrigger(false); diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/pad/mediapath/MediaPath.java b/PlayWallCore/src/main/java/de/tobias/playpad/pad/mediapath/MediaPath.java index 2287d7e5ac335b679cbf05975645bbd2e0737b97..64b1eb68e81f22260754f63fd089efdc97fcc12b 100644 --- a/PlayWallCore/src/main/java/de/tobias/playpad/pad/mediapath/MediaPath.java +++ b/PlayWallCore/src/main/java/de/tobias/playpad/pad/mediapath/MediaPath.java @@ -19,8 +19,8 @@ import java.util.UUID; */ public class MediaPath { - private UUID id; - private StringProperty fileName; + private final UUID id; + private final StringProperty fileName; private Pad pad; public MediaPath(UUID id, Path path, Pad pad) { diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/pad/preview/PadTextPreview.java b/PlayWallCore/src/main/java/de/tobias/playpad/pad/preview/PadTextPreview.java new file mode 100644 index 0000000000000000000000000000000000000000..714792cb845c075ea42c739964af5f1b44ef8fa7 --- /dev/null +++ b/PlayWallCore/src/main/java/de/tobias/playpad/pad/preview/PadTextPreview.java @@ -0,0 +1,40 @@ +package de.tobias.playpad.pad.preview; + +import de.tobias.playpad.pad.Pad; +import de.tobias.playpad.pad.view.IPadContentView; +import javafx.geometry.Pos; +import javafx.scene.Parent; +import javafx.scene.control.Label; +import javafx.scene.layout.Pane; +import javafx.scene.layout.Priority; +import javafx.scene.layout.VBox; +import javafx.scene.text.TextAlignment; + +public class PadTextPreview implements IPadContentView { + + private final Label nameLabel; + + public PadTextPreview(Pad pad, Pane parentNode) { + this.nameLabel = new Label(); + this.nameLabel.textProperty().bind(pad.nameProperty()); + + this.nameLabel.setWrapText(true); + this.nameLabel.setAlignment(Pos.CENTER); + this.nameLabel.setTextAlignment(TextAlignment.CENTER); + + this.nameLabel.prefWidthProperty().bind(parentNode.widthProperty()); + this.nameLabel.setMaxHeight(Double.MAX_VALUE); + + VBox.setVgrow(nameLabel, Priority.ALWAYS); + } + + @Override + public Parent getNode() { + return nameLabel; + } + + @Override + public void deInit() { + nameLabel.textProperty().unbind(); + } +} diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/pad/view/IPadContentView.java b/PlayWallCore/src/main/java/de/tobias/playpad/pad/view/IPadContentView.java index c91aa9c7651afdc804ed1e961bf8cf80a1744338..62c5c08f19d5f9dfae5feddd12b5020c1a0e4be6 100644 --- a/PlayWallCore/src/main/java/de/tobias/playpad/pad/view/IPadContentView.java +++ b/PlayWallCore/src/main/java/de/tobias/playpad/pad/view/IPadContentView.java @@ -1,6 +1,6 @@ package de.tobias.playpad.pad.view; -import javafx.scene.Node; +import javafx.scene.Parent; // TODO Rename to ContentView @@ -17,7 +17,7 @@ public interface IPadContentView { * * @return GUI Element */ - Node getNode(); + Parent getNode(); /** * Deinitialisiert die View. Hier können mögliche Bindings und Listener entfernt werden. diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/pad/view/IPadView.java b/PlayWallCore/src/main/java/de/tobias/playpad/pad/view/IPadView.java index 51c6ae1b2d7450bf0cd0c191d822bc7f43be7beb..fd2be672d536176e22eb2a1097acca69a666be8e 100644 --- a/PlayWallCore/src/main/java/de/tobias/playpad/pad/view/IPadView.java +++ b/PlayWallCore/src/main/java/de/tobias/playpad/pad/view/IPadView.java @@ -1,9 +1,10 @@ package de.tobias.playpad.pad.view; import de.tobias.playpad.pad.Pad; -import de.tobias.playpad.pad.viewcontroller.IPadViewController; +import de.tobias.playpad.pad.viewcontroller.AbstractPadViewController; import de.tobias.playpad.project.page.PadIndex; import javafx.css.PseudoClass; +import javafx.scene.control.Label; import javafx.scene.layout.Pane; /** @@ -33,7 +34,7 @@ public interface IPadView { * * @return ViewController des Pad */ - IPadViewController getViewController(); + AbstractPadViewController getViewController(); /** * Gibt das oberste GUI Element des Pads zurück, welche im MainView verwendet wird. @@ -106,6 +107,8 @@ public interface IPadView { */ void setPlaybarVisible(boolean visible); + Label getPlaylistLabel(); + /** * Fügt die Standart Elemente der PadView hinzu. Die GUI Element sind Abhängig vom Pad, und welchen Content es hat. * diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/pad/viewcontroller/AbstractPadViewController.java b/PlayWallCore/src/main/java/de/tobias/playpad/pad/viewcontroller/AbstractPadViewController.java new file mode 100644 index 0000000000000000000000000000000000000000..e1cbb5372b4768424ceae0a0f1cee17adf0a6527 --- /dev/null +++ b/PlayWallCore/src/main/java/de/tobias/playpad/pad/viewcontroller/AbstractPadViewController.java @@ -0,0 +1,71 @@ +package de.tobias.playpad.pad.viewcontroller; + +import de.tobias.playpad.pad.Pad; +import de.tobias.playpad.pad.content.Playlistable; +import de.tobias.playpad.pad.listener.IPadPositionListener; +import de.tobias.playpad.pad.view.IPadView; +import javafx.beans.binding.Bindings; +import javafx.beans.value.ChangeListener; +import javafx.util.Duration; + +/** + * Schnittstellen um mit einem PadViewController zu kommunizieren. + * + * @author tobias + * @since 5.1.0 + */ +public abstract class AbstractPadViewController { + + /** + * Gibt das Pad zurück, welches er verwaltet. (Das Datenmodel) + * + * @return Pad + */ + public abstract Pad getPad(); + + /** + * Gibt die View des Controllers zurück. + * + * @return View + */ + public abstract IPadView getView(); + + /** + * Setzt ein Pad für ein View. Hier werden die Datein mittels ViewController der View bekannt gemacht. + * + * @param pad Neues Pad + */ + public abstract void setupPad(Pad pad); + + /** + * Entfertn des Verbundene Pad von der View. + */ + public abstract void removePad(); + + public abstract void updateTimeLabel(); + + public abstract void updateButtonDisable(); + + public abstract IPadPositionListener getPadPositionListener(); + + public abstract ChangeListener<Duration> getPadDurationListener(); + + public void updatePlaylistLabelBinding(Pad pad) { + if (pad.getContent() instanceof Playlistable) { + final Playlistable content = (Playlistable) pad.getContent(); + getView().getPlaylistLabel().textProperty().bind(Bindings.createStringBinding(() -> { + final int currentPlayingMediaIndex = content.getCurrentPlayingMediaIndex(); + final int totalCount = pad.getPaths().size(); + + if (currentPlayingMediaIndex < 0) { + return "- / " + totalCount; + } else { + return (currentPlayingMediaIndex + 1) + " / " + totalCount; + } + }, content.currentPlayingMediaIndexProperty(), pad.getPaths())); + } else { + getView().getPlaylistLabel().textProperty().unbind(); + getView().getPlaylistLabel().setText(""); + } + } +} diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/pad/viewcontroller/IPadViewController.java b/PlayWallCore/src/main/java/de/tobias/playpad/pad/viewcontroller/IPadViewController.java deleted file mode 100644 index 6c010518809856910286625e81179040dfcb5c13..0000000000000000000000000000000000000000 --- a/PlayWallCore/src/main/java/de/tobias/playpad/pad/viewcontroller/IPadViewController.java +++ /dev/null @@ -1,50 +0,0 @@ -package de.tobias.playpad.pad.viewcontroller; - -import de.tobias.playpad.pad.Pad; -import de.tobias.playpad.pad.listener.IPadPositionListener; -import de.tobias.playpad.pad.view.IPadView; -import javafx.beans.value.ChangeListener; -import javafx.util.Duration; - -/** - * Schnittstellen um mit einem PadViewController zu kommunizieren. - * - * @author tobias - * @since 5.1.0 - */ -public interface IPadViewController { - - /** - * Gibt das Pad zurück, welches er verwaltet. (Das Datenmodel) - * - * @return Pad - */ - Pad getPad(); - - /** - * Gibt die View des Controllers zurück. - * - * @return View - */ - IPadView getView(); - - /** - * Setzt ein Pad für ein View. Hier werden die Datein mittels ViewController der View bekannt gemacht. - * - * @param pad Neues Pad - */ - void setupPad(Pad pad); - - /** - * Entfertn des Verbundene Pad von der View. - */ - void removePad(); - - void updateTimeLabel(); - - void updateButtonDisable(); - - IPadPositionListener getPadPositionListener(); - - ChangeListener<Duration> getPadDurationListener(); -} diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/profile/Profile.java b/PlayWallCore/src/main/java/de/tobias/playpad/profile/Profile.java index 4900d036b2a09c0e936c85cbc6e5b52bebea9964..7e50c4dc4866467c22a06cc7372e830b28cd36e7 100644 --- a/PlayWallCore/src/main/java/de/tobias/playpad/profile/Profile.java +++ b/PlayWallCore/src/main/java/de/tobias/playpad/profile/Profile.java @@ -17,9 +17,7 @@ import org.dom4j.DocumentException; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; +import java.util.*; public class Profile { @@ -30,11 +28,13 @@ public class Profile { private static Profile currentProfile; // Settings - private ProfileReference ref; + private final ProfileReference ref; private ProfileSettings profileSettings; private MappingCollection mappings; + private Map<String, Object> customSettings; + /** * Use {@link ProfileReferenceManager#addProfile(ProfileReference)} instead * @@ -44,6 +44,7 @@ public class Profile { this.ref = ref; this.profileSettings = new ProfileSettings(); this.mappings = new MappingCollection(); + this.customSettings = new HashMap<>(); } public static Mapping createMappingWithDefaultActions() { @@ -84,6 +85,14 @@ public class Profile { return profileSettings; } + public Object getCustomSettings(String name) { + return customSettings.get(name); + } + + public void addCustomSettings(String name, Object settings) { + customSettings.put(name, settings); + } + public static Profile load(ProfileReference ref) throws DocumentException, IOException, ProfileNotFoundException { if (ref == null) { throw new IllegalArgumentException("Profile is null"); diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/tigger/Trigger.java b/PlayWallCore/src/main/java/de/tobias/playpad/tigger/Trigger.java index fcfb8301d15073275508a66799bdc0f2a20244f1..ba284a16cccbe20166ac9c86c41fbb9a822dc5e5 100644 --- a/PlayWallCore/src/main/java/de/tobias/playpad/tigger/Trigger.java +++ b/PlayWallCore/src/main/java/de/tobias/playpad/tigger/Trigger.java @@ -104,20 +104,22 @@ public class Trigger { return triggerPoint.name() + " (" + items.size() + ")"; } - public void handle(Pad pad, Duration duration, Project project, IMainViewController mainViewController, Profile currentProfile) { + public void handle(Pad pad, Duration currentDuration, Project project, IMainViewController mainViewController, Profile currentProfile) { for (TriggerItem item : items) { if (triggerPoint == TriggerPoint.START) { - handleStartPoint(pad, duration, project, mainViewController, currentProfile, item); + handleStartPoint(pad, currentDuration, project, mainViewController, currentProfile, item); } else if (triggerPoint == TriggerPoint.STOP) { - handleEndPoint(pad, duration, project, mainViewController, currentProfile, item); + handleEndPoint(pad, currentDuration, project, mainViewController, currentProfile, item); } else if (triggerPoint == TriggerPoint.EOF) { - handleEndPoint(pad, duration, project, mainViewController, currentProfile, item); + handleEndPoint(pad, currentDuration, project, mainViewController, currentProfile, item); + } else if (triggerPoint == TriggerPoint.EOF_STATE) { + item.performAction(pad, project, mainViewController, currentProfile); } } } private void handleEndPoint(Pad pad, Duration duration, Project project, IMainViewController mainViewController, Profile currentProfile, TriggerItem item) { - // Wenn Trigger noch nicht gespiel wurde (null) und Zeit größer ist als gesetzte Zeit (oder 0) + // Wenn Trigger noch nicht gespielt wurde (null) und Zeit größer ist als gesetzte Zeit (oder 0) if (item.getPerformedAt() == null && (item.getDurationFromPoint().greaterThan(duration) || duration.equals(Duration.ZERO))) { item.performAction(pad, project, mainViewController, currentProfile); item.setPerformedAt(duration); @@ -128,7 +130,7 @@ public class Trigger { private void handleStartPoint(Pad pad, Duration duration, Project project, IMainViewController mainViewController, Profile currentProfile, TriggerItem item) { if (pad.getStatus() == PadStatus.PLAY) { - // Mitten drin, wenn die Zeit die gespiel wurde größer ist als die gesetzte und noch der Trigger noch nicht ausgeführt + // Mitten drin, wenn die Zeit die gespielt wurde größer ist als die gesetzte und noch der Trigger noch nicht ausgeführt // wurde (null) if ((item.getPerformedAt() == null && item.getDurationFromPoint().lessThan(duration)) // Wenn der Trigger am Anfang ist diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/tigger/TriggerItemFactory.java b/PlayWallCore/src/main/java/de/tobias/playpad/tigger/TriggerItemFactory.java index 9590d921a24a86a84453ff83acbcce147cb69c91..118dc56017638413688062a0bd742f59ffc05dec 100644 --- a/PlayWallCore/src/main/java/de/tobias/playpad/tigger/TriggerItemFactory.java +++ b/PlayWallCore/src/main/java/de/tobias/playpad/tigger/TriggerItemFactory.java @@ -2,6 +2,7 @@ package de.tobias.playpad.tigger; import de.thecodelabs.utils.ui.NVC; import de.tobias.playpad.registry.Component; +import de.tobias.playpad.viewcontroller.main.IMainViewController; public abstract class TriggerItemFactory extends Component { @@ -11,6 +12,6 @@ public abstract class TriggerItemFactory extends Component { public abstract TriggerItem newInstance(Trigger trigger); - public abstract NVC getSettingsController(TriggerItem item); + public abstract NVC getSettingsController(TriggerItem item, IMainViewController mainViewController); } diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/tigger/TriggerPoint.java b/PlayWallCore/src/main/java/de/tobias/playpad/tigger/TriggerPoint.java index c25fb939321d8b292cf5db86ea7bd7cf3ee7c10d..4f1390d9e6ebda63f92fe7647d00ae0fe759df2c 100644 --- a/PlayWallCore/src/main/java/de/tobias/playpad/tigger/TriggerPoint.java +++ b/PlayWallCore/src/main/java/de/tobias/playpad/tigger/TriggerPoint.java @@ -2,8 +2,21 @@ package de.tobias.playpad.tigger; public enum TriggerPoint { - START, - STOP, - EOF + START(true), + STOP(false), + EOF(true), + EOF_STATE(false); + /** + * Defines if a trigger can be run after, before a certain event. + */ + private final boolean timeAppendable; + + TriggerPoint(boolean timeAppendable) { + this.timeAppendable = timeAppendable; + } + + public boolean isTimeAppendable() { + return timeAppendable; + } } diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/view/main/MainLayoutFactory.java b/PlayWallCore/src/main/java/de/tobias/playpad/view/main/MainLayoutFactory.java index f7cf7399636ec98235da323c9e75f1c23a0631f1..88039dcb374505f9f913fc81b4d72d5c20fdfd63 100644 --- a/PlayWallCore/src/main/java/de/tobias/playpad/view/main/MainLayoutFactory.java +++ b/PlayWallCore/src/main/java/de/tobias/playpad/view/main/MainLayoutFactory.java @@ -1,7 +1,7 @@ package de.tobias.playpad.view.main; import de.tobias.playpad.pad.view.IPadView; -import de.tobias.playpad.pad.viewcontroller.IPadViewController; +import de.tobias.playpad.pad.viewcontroller.AbstractPadViewController; import de.tobias.playpad.registry.Component; import de.tobias.playpad.viewcontroller.main.IMainViewController; import de.tobias.playpad.viewcontroller.main.MenuToolbarViewController; @@ -31,7 +31,7 @@ public abstract class MainLayoutFactory extends Component { * * @return Pad * @see IPadView notwendige Methoden für ein Pad - * @see IPadViewController ViewController zum Pad + * @see AbstractPadViewController ViewController zum Pad */ public abstract IPadView createPadView(); diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/view/main/ProjectPreviewView.java b/PlayWallCore/src/main/java/de/tobias/playpad/view/main/ProjectPreviewView.java index 10bba54fef944f0b3dd71fe6a651bf867b4a54f7..2e16c3cb79abe3471b9e6c0c185852f9efb87816 100644 --- a/PlayWallCore/src/main/java/de/tobias/playpad/view/main/ProjectPreviewView.java +++ b/PlayWallCore/src/main/java/de/tobias/playpad/view/main/ProjectPreviewView.java @@ -6,6 +6,9 @@ import de.tobias.playpad.project.Project; import de.tobias.playpad.project.page.Page; import javafx.collections.FXCollections; import javafx.collections.ObservableList; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.Node; import javafx.scene.control.Pagination; import javafx.scene.control.ToggleButton; import javafx.scene.control.Tooltip; @@ -15,31 +18,36 @@ import java.util.List; public class ProjectPreviewView extends Pagination { - private Project project; + private final Project project; + private final ObservableList<Pad> selected; - private ObservableList<Pad> selected; - - public ProjectPreviewView(Project project, List<Pad> preSelect) { + public ProjectPreviewView(Project project, List<Pad> preSelect, int initialPage) { super(project.getPages().size()); this.project = project; this.selected = FXCollections.observableArrayList(preSelect); - setPageFactory(index -> { - GridPane gridPane = new GridPane(); - gridPane.setHgap(7); - gridPane.setVgap(7); + setCurrentPageIndex(initialPage); + setPageFactory(this::getPageNode); + } + + private Node getPageNode(int index) { + GridPane gridPane = new GridPane(); + gridPane.setHgap(7); + gridPane.setVgap(7); + gridPane.setAlignment(Pos.CENTER); - Page page = project.getPage(index); - for (int x = 0; x < project.getSettings().getColumns(); x++) { - for (int y = 0; y < project.getSettings().getRows(); y++) { - final Pad pad = page.getPad(x, y); - ToggleButton toggleButton = getToggleButton(preSelect, pad); + gridPane.setPadding(new Insets(0, 0, 7, 0)); - gridPane.add(toggleButton, x, y); - } + final Page page = project.getPage(index); + for (int x = 0; x < project.getSettings().getColumns(); x++) { + for (int y = 0; y < project.getSettings().getRows(); y++) { + final Pad pad = page.getPad(x, y); + ToggleButton toggleButton = getToggleButton(selected, pad); + + gridPane.add(toggleButton, x, y); } - return gridPane; - }); + } + return gridPane; } private ToggleButton getToggleButton(List<Pad> preSelect, Pad pad) { diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/pom.xml b/PlayWallPlugins/PlayWallPluginContentPlayer/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..f888ba1cdefe38357b672f8f58debe259874dbf7 --- /dev/null +++ b/PlayWallPlugins/PlayWallPluginContentPlayer/pom.xml @@ -0,0 +1,131 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <artifactId>PlayWallPlugins</artifactId> + <groupId>de.tobias.playpad</groupId> + <version>7.0.2</version> + </parent> + <modelVersion>4.0.0</modelVersion> + + <artifactId>PlayWallPluginContentPlayer</artifactId> + + <properties> + <project.outputDirectory>../../build/${project.version}</project.outputDirectory> + <project.artifactName>${project.artifactId}-v${project.version}</project.artifactName> + </properties> + + <dependencies> + <dependency> + <groupId>de.tobias.playpad</groupId> + <artifactId>PlayWallCore</artifactId> + <version>${project.version}</version> + <scope>provided</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>de.thecodelabs</groupId> + <artifactId>versionizer-maven-plugin</artifactId> + <version>${versionizer-maven-plugin.version}</version> + <configuration> + <resourceFile>plugin.yml</resourceFile> + </configuration> + <executions> + <execution> + <id>resource-fill</id> + <goals> + <goal>resource-fill</goal> + </goals> + <phase>compile</phase> + </execution> + </executions> + </plugin> + + <!--Scala Plugin--> + <plugin> + <groupId>net.alchim31.maven</groupId> + <artifactId>scala-maven-plugin</artifactId> + <executions> + <execution> + <id>scala-compile-first</id> + <phase>process-resources</phase> + <goals> + <goal>add-source</goal> + <goal>compile</goal> + </goals> + </execution> + <execution> + <id>scala-test-compile</id> + <phase>process-test-resources</phase> + <goals> + <goal>testCompile</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-assembly-plugin</artifactId> + <version>3.1.1</version> + <configuration> + <descriptorRefs> + <descriptorRef>jar-with-dependencies</descriptorRef> + </descriptorRefs> + <outputDirectory>${project.outputDirectory}</outputDirectory> + <finalName>${project.artifactName}</finalName> + <appendAssemblyId>false</appendAssemblyId> + </configuration> + <executions> + <execution> + <id>assemble-all</id> + <phase>package</phase> + <goals> + <goal>single</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> + + <distributionManagement> + <repository> + <id>release</id> + <name>TheCodeLabs-releases</name> + <url>https://maven.thecodelabs.de/artifactory/TheCodeLabs-release</url> + </repository> + <snapshotRepository> + <id>snapshots</id> + <name>TheCodeLabs-snapshots</name> + <url>https://maven.thecodelabs.de/artifactory/TheCodeLabs-snapshots</url> + </snapshotRepository> + </distributionManagement> + + <repositories> + <repository> + <id>release</id> + <url>https://maven.thecodelabs.de/artifactory/TheCodeLabs-release</url> + <releases> + <enabled>true</enabled> + </releases> + <snapshots> + <enabled>false</enabled> + </snapshots> + </repository> + <repository> + <id>snapshots</id> + <url>https://maven.thecodelabs.de/artifactory/TheCodeLabs-snapshots</url> + <releases> + <enabled>false</enabled> + </releases> + <snapshots> + <enabled>true</enabled> + </snapshots> + </repository> + </repositories> + +</project> \ No newline at end of file diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/PadContent.xml b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/PadContent.xml new file mode 100644 index 0000000000000000000000000000000000000000..32da3b2a4dcc9e49a66cbac1a9bb23b5528c2a53 --- /dev/null +++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/PadContent.xml @@ -0,0 +1,6 @@ +<Actions> + <Component id="content_player" name="plugin.content.Player.name" icon="MONITOR_MULTIPLE" + class="de.thecodelabs.utils.ui.icon.MaterialDesignIcon" + size="30">de.tobias.playpad.plugin.content.pad.ContentPlayerPadContentFactory + </Component> +</Actions> \ No newline at end of file diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/lang/base.properties b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/lang/base.properties new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/lang/base_de.properties b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/lang/base_de.properties new file mode 100644 index 0000000000000000000000000000000000000000..f349884fe1a09c06e16bd93793838ac6430afbb8 --- /dev/null +++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/lang/base_de.properties @@ -0,0 +1,17 @@ +plugin.content.Player.name=Content Player + +plugin.content.player.settings=Content Player +plugin.content.player.settings.name=Name: +plugin.content.player.settings.x=X: +plugin.content.player.settings.y=Y: +plugin.content.player.settings.width=Breite: +plugin.content.player.settings.height=H\u00F6he: +plugin.content.player.settings.add=Hinzuf\u00FCgen +plugin.content.player.settings.remove=L\u00F6schen +plugin.content.player.settings.default_name=Zone + +plugin.content.pad.settings.lastFrame.label=Letztes Frame halten +plugin.content.pad.settings.lastFrame.checkbox=Aktiv +plugin.content.pad.settings.zone=Zonen +plugin.content.pad.settings.zone.addAll=Alle anw\u00E4hlen +plugin.content.pad.settings.zone.removeAll=Alle abw\u00E4hlen diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/plugin.yml b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/plugin.yml new file mode 100644 index 0000000000000000000000000000000000000000..70a18e12c0441bf59f78ee543ce885c15034c894 --- /dev/null +++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/plugin.yml @@ -0,0 +1,6 @@ +main: "de.tobias.playpad.plugin.content.ContentPluginMain" +name: "ContentPlugin" +artifactId: "${pom.artifactId}" +groupId: "${pom.groupId}" +version: "${pom.version}" +build: 1 diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/view/ContentPadSettings.fxml b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/view/ContentPadSettings.fxml new file mode 100644 index 0000000000000000000000000000000000000000..bfd7033978d027d36f881bb9957b7c158056cb89 --- /dev/null +++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/view/ContentPadSettings.fxml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<?import javafx.geometry.Insets?> +<?import javafx.scene.control.*?> +<?import javafx.scene.layout.*?> +<?import org.controlsfx.control.CheckListView?> +<AnchorPane prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"> + <children> + <VBox layoutX="237.0" layoutY="-18.0" spacing="14.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"> + <children> + <HBox alignment="CENTER_LEFT" spacing="14.0"> + <children> + <Label alignment="CENTER_RIGHT" prefWidth="150.0" text="%plugin.content.pad.settings.lastFrame.label" /> + <CheckBox fx:id="lastFrameCheckbox" mnemonicParsing="false" text="%plugin.content.pad.settings.lastFrame.checkbox" /> + </children> + </HBox> + <HBox spacing="14.0"> + <children> + <Label alignment="CENTER_RIGHT" prefWidth="150.0" text="%plugin.content.pad.settings.zone" /> + <CheckListView fx:id="zoneListView" prefHeight="150.0" /> + <VBox spacing="14.0"> + <children> + <Button fx:id="addAllZonesButton" onAction="#onAddAllZonesHandler" mnemonicParsing="false" text="%plugin.content.pad.settings.zone.addAll" /> + <Button fx:id="removeAllZonesButton" onAction="#onRemoveAllZonesHandler" mnemonicParsing="false" text="%plugin.content.pad.settings.zone.removeAll" /> + </children> + </VBox> + </children> + </HBox> + </children> + <padding> + <Insets bottom="14.0" left="14.0" right="14.0" top="14.0" /> + </padding> + </VBox> + </children> +</AnchorPane> diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/view/PlayerView.fxml b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/view/PlayerView.fxml new file mode 100644 index 0000000000000000000000000000000000000000..3428c4ea773b6b4a8e3838f54f9373d77107c718 --- /dev/null +++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/view/PlayerView.fxml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> + + +<?import javafx.scene.layout.Pane?> +<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" /> diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/view/ZoneSettings.fxml b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/view/ZoneSettings.fxml new file mode 100644 index 0000000000000000000000000000000000000000..64036902597eb22c98b1b9881eee56e6aad82a12 --- /dev/null +++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/view/ZoneSettings.fxml @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<?import de.thecodelabs.utils.ui.scene.input.NumberTextField?> +<?import javafx.geometry.Insets?> +<?import javafx.scene.control.*?> +<?import javafx.scene.layout.*?> +<AnchorPane prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"> + <children> + <VBox layoutY="345.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.topAnchor="0.0"> + <children> + <ListView fx:id="listView" prefWidth="250.0" VBox.vgrow="ALWAYS" /> + <HBox prefHeight="30.0" prefWidth="200.0" spacing="14.0"> + <children> + <Button fx:id="addButton" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#onAddHandle" text="%plugin.content.player.settings.add" HBox.hgrow="ALWAYS" /> + <Button fx:id="removeButton" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#onRemoveHandle" text="%plugin.content.player.settings.remove" HBox.hgrow="ALWAYS" /> + </children> + <padding> + <Insets bottom="14.0" left="14.0" right="14.0" top="14.0" /> + </padding> + </HBox> + </children> + </VBox> + <VBox layoutX="200.0" prefHeight="400.0" prefWidth="401.0" spacing="14.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="250.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"> + <children> + <HBox alignment="CENTER_LEFT" spacing="14.0"> + <children> + <Label alignment="CENTER_RIGHT" prefWidth="150.0" text="%plugin.content.player.settings.name" /> + <TextField fx:id="nameTextField" /> + </children> + </HBox> + <HBox alignment="CENTER_LEFT" spacing="14.0"> + <children> + <Label alignment="CENTER_RIGHT" prefWidth="150.0" text="%plugin.content.player.settings.x" /> + <NumberTextField fx:id="xTextField" /> + </children> + </HBox> + <HBox alignment="CENTER_LEFT" spacing="14.0"> + <children> + <Label alignment="CENTER_RIGHT" prefWidth="150.0" text="%plugin.content.player.settings.y" /> + <NumberTextField fx:id="yTextField" /> + </children> + </HBox> + <HBox alignment="CENTER_LEFT" spacing="14.0"> + <children> + <Label alignment="CENTER_RIGHT" prefWidth="150.0" text="%plugin.content.player.settings.width" /> + <NumberTextField fx:id="widthTextField" /> + </children> + </HBox> + <HBox alignment="CENTER_LEFT" spacing="14.0"> + <children> + <Label alignment="CENTER_RIGHT" prefWidth="150.0" text="%plugin.content.player.settings.height" /> + <NumberTextField fx:id="heightTextField" /> + </children> + </HBox> + </children> + <padding> + <Insets bottom="14.0" left="14.0" right="14.0" top="14.0" /> + </padding> + </VBox> + </children> +</AnchorPane> diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/ContentPluginMain.scala b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/ContentPluginMain.scala new file mode 100644 index 0000000000000000000000000000000000000000..2756703ed75e039767b9c5ba3e09324f46653b98 --- /dev/null +++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/ContentPluginMain.scala @@ -0,0 +1,60 @@ +package de.tobias.playpad.plugin.content + +import de.thecodelabs.plugins.PluginDescriptor +import de.thecodelabs.storage.settings.{Storage, StorageTypes} +import de.thecodelabs.utils.util.Localization +import de.tobias.playpad.PlayPadPlugin +import de.tobias.playpad.plugin.content.player.ContentPlayerViewController +import de.tobias.playpad.plugin.content.settings.{ZoneConfiguration, ZoneSettingsViewController} +import de.tobias.playpad.plugin.{Module, PlayPadPluginStub, SettingsListener} +import de.tobias.playpad.profile.{Profile, ProfileListener} +import javafx.application.Platform + +class ContentPluginMain extends PlayPadPluginStub with SettingsListener with ProfileListener { + + private var module: Module = _ + + override def startup(descriptor: PluginDescriptor): Unit = { + module = new Module(descriptor.getName, descriptor.getArtifactId) + + val localization = Localization.loadBundle("lang/base", getClass.getClassLoader) + Localization.addResourceBundle(localization) + + PlayPadPlugin.getRegistries.getPadContents.loadComponentsFromFile("PadContent.xml", getClass.getClassLoader, module, localization) + PlayPadPlugin.getInstance().addAdditionalProfileSettingsTab(() => new ZoneSettingsViewController) + + PlayPadPlugin.getInstance().addSettingsListener(this) + Profile.registerListener(this) + } + + override def shutdown(): Unit = { + ContentPluginMain.playerViewController.getStageContainer.ifPresent(container => container.forceClose()) + } + + override def getModule: Module = module + + override def onLoad(profile: Profile): Unit = { + val path = profile.getRef.getCustomFilePath("Zones.json") + val zoneConfiguration = Storage.load(path, StorageTypes.JSON, classOf[ZoneConfiguration]) + profile.addCustomSettings(ContentPluginMain.zoneConfigurationKey, zoneConfiguration) + } + + override def onSave(profile: Profile): Unit = { + val path = profile.getRef.getCustomFilePath("Zones.json") + val zoneConfigurationObject = profile.getCustomSettings(ContentPluginMain.zoneConfigurationKey) + if (zoneConfigurationObject != null) { + Storage.save(path, StorageTypes.JSON, zoneConfigurationObject) + } + } + + override def reloadSettings(oldProfile: Profile, currentProfile: Profile): Unit = { + val zoneConfiguration = currentProfile.getCustomSettings(ContentPluginMain.zoneConfigurationKey).asInstanceOf[ZoneConfiguration] + Platform.runLater(() => ContentPluginMain.playerViewController.configurePlayers(zoneConfiguration)) + } +} + +object ContentPluginMain { + lazy val playerViewController: ContentPlayerViewController = new ContentPlayerViewController + + val zoneConfigurationKey = "ZoneConfiguration" +} \ No newline at end of file diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerMediaContainer.scala b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerMediaContainer.scala new file mode 100644 index 0000000000000000000000000000000000000000..3c298dcbfb2fcf167ef3d682cf03bb71c3d6c9da --- /dev/null +++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerMediaContainer.scala @@ -0,0 +1,57 @@ +package de.tobias.playpad.plugin.content.pad + +import de.tobias.playpad.pad.PadStatus +import de.tobias.playpad.pad.mediapath.MediaPath +import de.tobias.playpad.plugin.content.ContentPluginMain +import de.tobias.playpad.plugin.content.util._ +import javafx.scene.media.MediaPlayer +import javafx.util.Duration + +class ContentPlayerMediaContainer(val content: ContentPlayerPadContent, val path: MediaPath, val mediaPlayer: MediaPlayer) { + def play(): Unit = { + content._durationProperty.bind(mediaPlayer.totalDurationProperty()) + content._positionProperty.bind(mediaPlayer.currentTimeProperty()) + ContentPluginMain.playerViewController.showMediaPlayer(content.getPad.getPadIndex, mediaPlayer, content.getSelectedZones) + + mediaPlayer.seek(Duration.ZERO) + mediaPlayer.play() + + content.getPad.setEof(false) + content.currentPlayingMediaIndexProperty().set(content.getMediaPlayers.indexOf(this)) + } + + def resume(): Unit = { + mediaPlayer.play() + } + + def pause(): Unit = { + mediaPlayer.pause() + } + + def next(): Unit = { + stop() + + val players = content.getMediaPlayers + val index = players.indexOf(this) + content.currentPlayingMediaIndexProperty().set(index) + + if (index + 1 < players.length) { + players(index + 1).play() + } else if (content.getPad.getPadSettings.isLoop) { + players.head.play() + } else { + content.getPad.setStatus(PadStatus.STOP) + } + } + + def stop(): Unit = { + mediaPlayer.stop() + ContentPluginMain.playerViewController.disconnectMediaPlayer(mediaPlayer, content.getSelectedZones) + + content._durationProperty.bind(content.totalDurationBinding()) + content._positionProperty.unbind() + content._positionProperty.set(Duration.ZERO) + } + + override def toString: String = f"MediaPlayerContainer: $path" +} \ No newline at end of file diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerPadContent.scala b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerPadContent.scala new file mode 100644 index 0000000000000000000000000000000000000000..c1db519a17b8119362c64e7385ba26d91f2b9a40 --- /dev/null +++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerPadContent.scala @@ -0,0 +1,322 @@ +package de.tobias.playpad.plugin.content.pad + +import de.thecodelabs.logger.Logger +import de.tobias.playpad.pad.content.play.{Durationable, Pauseable} +import de.tobias.playpad.pad.content.{PadContent, Playlistable} +import de.tobias.playpad.pad.fade.{Fadeable, LinearFadeController} +import de.tobias.playpad.pad.mediapath.MediaPath +import de.tobias.playpad.pad.{Pad, PadStatus} +import de.tobias.playpad.plugin.content.ContentPluginMain +import de.tobias.playpad.plugin.content.settings.{Zone, ZoneConfiguration} +import de.tobias.playpad.plugin.content.util._ +import de.tobias.playpad.profile.Profile +import de.tobias.playpad.volume.VolumeManager +import javafx.application.Platform +import javafx.beans.binding.{Bindings, ObjectBinding} +import javafx.beans.property._ +import javafx.collections.{FXCollections, ObservableList} +import javafx.scene.media.{Media, MediaPlayer} +import javafx.util.Duration + +import java.nio.file.Files +import java.util +import java.util.Optional +import java.util.stream.Collectors +import scala.jdk.CollectionConverters._ + +class ContentPlayerPadContent(val pad: Pad, val `type`: String) extends PadContent(pad) with Pauseable with Durationable with Playlistable with Fadeable { + + private val mediaPlayers: ObservableList[ContentPlayerMediaContainer] = FXCollections.observableArrayList() + private val currentRunningIndexProperty: IntegerProperty = new SimpleIntegerProperty(-1) + + private[pad] val _durationProperty = new SimpleObjectProperty[Duration] + private[pad] val _positionProperty = new SimpleObjectProperty[Duration] + + private var showingLastFrame: Boolean = false + private var isPause: Boolean = false + + private val fadeController = new LinearFadeController(value => { + if (getCurrentPlayingMediaIndex >= 0) { + ContentPluginMain.playerViewController + .setFadeValue(mediaPlayers(getCurrentPlayingMediaIndex).mediaPlayer, getSelectedZones, value) + } + }) + + override def getType: String = `type` + + override def getCurrentPlayingMediaIndex: Int = currentRunningIndexProperty.get() + + override def currentPlayingMediaIndexProperty(): IntegerProperty = currentRunningIndexProperty + + def getMediaPlayers: ObservableList[ContentPlayerMediaContainer] = mediaPlayers + + override def hasNext: Boolean = getCurrentPlayingMediaIndex + 1 < mediaPlayers.length + + /* + Control Methods + */ + + override def play(): Unit = { + if (isPause) { + mediaPlayers(getCurrentPlayingMediaIndex).resume() + } else { + ContentPluginMain.playerViewController.addActivePadToList(getPad.getPadIndex, getSelectedZones) + + getPad.setEof(false) + mediaPlayers.head.play() + } + showingLastFrame = false + isPause = false + } + + override def pause(): Unit = { + isPause = true + mediaPlayers(getCurrentPlayingMediaIndex).pause() + } + + override def next(): Unit = { + mediaPlayers(getCurrentPlayingMediaIndex).next() + } + + override def stop(): Boolean = { + isPause = false + mediaPlayers(getCurrentPlayingMediaIndex).stop() + currentRunningIndexProperty.set(-1) + + ContentPluginMain.playerViewController.removeActivePadFromList(getPad.getPadIndex, getSelectedZones) + true + } + + def onEof(): Unit = { + if (isFadeActive) { + ContentPluginMain.playerViewController.removeActivePadFromList(getPad.getPadIndex, getSelectedZones) + return + } + + if (shouldShowLastFrame() && !showingLastFrame // Only is settings is enabled and not already in last frame state + && !pad.getPadSettings.isLoop // Only go to last frame state, is looping is disabled + ) { + getPad.setStatus(PadStatus.PAUSE) + showingLastFrame = true + return + } + + showingLastFrame = false + + if (getPad.isEof) { + mediaPlayers(getCurrentPlayingMediaIndex).next() + return + } + } + + /* + Durationable + */ + + override def getDuration: Duration = _durationProperty.get() + + override def getPosition: Duration = _positionProperty.get() + + override def durationProperty(): ReadOnlyObjectProperty[Duration] = _durationProperty + + override def positionProperty(): ReadOnlyObjectProperty[Duration] = _positionProperty + + def totalDurationBinding(): ObjectBinding[Duration] = { + Bindings.createObjectBinding(() => mediaPlayers.stream() + .map(player => player.mediaPlayer.getTotalDuration) + .filter(duration => duration != null) + .reduce(Duration.ZERO, (o1: Duration, o2: Duration) => o1.add(o2)), + mediaPlayers.stream().map(player => { + if (player.mediaPlayer != null) { + player.mediaPlayer.totalDurationProperty() + } else { + null + } + }) + .filter(o => o != null) + .toArray(size => new Array[ReadOnlyObjectProperty[Duration]](size)): _*) + } + + /* + Fadeable + */ + + override def fadeIn(): Unit = { + val fadeIn = getPad.getPadSettings.getFade.getFadeIn + if (fadeIn.toMillis > 0) { + fadeController.fadeIn(fadeIn) + } + } + + override def fadeOut(onFinish: Runnable): Unit = { + val fadeOut = getPad.getPadSettings.getFade.getFadeOut + if (fadeOut.toMillis > 0) { + fadeController.fadeOut(fadeOut, () => { + if (onFinish != null) onFinish.run() + + if (getPad.getStatus == PadStatus.PLAY) { + getPad.setStatus(PadStatus.STOP) + } + }) + } + else { + onFinish.run() + } + } + + override def isFadeActive: Boolean = fadeController.isFading + + override def fade(from: Double, to: Double, duration: Duration, onFinish: Runnable): Unit = { + fadeController.fade(from, to, duration, onFinish) + } + + /* + Loading + */ + + override def isPadLoaded: Boolean = { + mediaPlayers.isNotEmpty && !mediaPlayers.stream().anyMatch(player => player.mediaPlayer.getStatus == MediaPlayer.Status.UNKNOWN) + } + + override def isLoaded(mediaPath: MediaPath): Boolean = { + val loadedOptional: Optional[Boolean] = mediaPlayers.stream() + .filter(item => item.path == mediaPath) + .findFirst() + .map(container => container.mediaPlayer.getStatus != MediaPlayer.Status.UNKNOWN) + loadedOptional.orElse(false) + } + + + override def isPadLoading: Boolean = mediaPlayers.stream().anyMatch(player => player.mediaPlayer.getStatus == MediaPlayer.Status.UNKNOWN) + + /** + * Load media files. + */ + override def loadMedia(): Unit = { + mediaPlayers.clear() + getPad.getPaths.forEach(loadMedia(_)) + } + + /** + * Load media file. + * + * @param mediaPath specify media path + */ + override def loadMedia(mediaPath: MediaPath): Unit = { + val path = mediaPath.getPath + if (Files.notExists(path)) { + Platform.runLater(() => getPad.setStatus(PadStatus.NOT_FOUND)) + return + } + + val media = new Media(path.toUri.toString) + val mediaPlayer = new MediaPlayer(media) + + mediaPlayer.setOnReady(() => { + getPad.setStatus(PadStatus.READY) + + _durationProperty.bind(totalDurationBinding()) + _positionProperty.set(Duration.ZERO) + + Platform.runLater(() => { + if (getPad.isPadVisible) { + getPad.getController.getView.showBusyView(false) + } + }) + }) + + mediaPlayer.errorProperty().addListener((_, _, newValue) => Platform.runLater(() => { + Logger.error(newValue) + pad.setStatus(PadStatus.ERROR) + })) + mediaPlayer.setOnError(() => Platform.runLater(() => { + if (getPad.isPadVisible) { + getPad.getController.getView.showBusyView(false) + } + Logger.error(mediaPlayer.getError) + pad.setStatus(PadStatus.ERROR) + })) + + mediaPlayer.setOnEndOfMedia(() => { + getPad.setEof(true) + onEof() + }) + + mediaPlayers.add(new ContentPlayerMediaContainer(this, mediaPath, mediaPlayer)) + } + + /** + * Unload media files. + */ + override def unloadMedia(): Unit = { + if ((getPad.getStatus eq PadStatus.PLAY) || (getPad.getStatus eq PadStatus.PAUSE)) getPad.setStatus(PadStatus.STOP) + + mediaPlayers.clear() + + Platform.runLater(() => { + if (getPad != null) { + getPad.setStatus(PadStatus.EMPTY) + } + }) + } + + /** + * Unload media file. + * + * @param mediaPath specify media path + */ + override def unloadMedia(mediaPath: MediaPath): Unit = { + val index = mediaPlayers.indexWhere(item => item.path.getId == mediaPath.getId) + + if (index >= 0) { + val playerContainer = mediaPlayers(index) + playerContainer.stop() + mediaPlayers.remove(index) + } + } + + override def reorderMedia(): Unit = { + val paths = pad.getPaths + mediaPlayers.sort((o1, o2) => Integer.compare(paths.indexOf(o1.path), paths.indexOf(o2.path))) + } + + /* + Volume + */ + + override def updateVolume(): Unit = { + val volume = VolumeManager.getInstance.computeVolume(getPad) + mediaPlayers.forEach(player => player.mediaPlayer.setVolume(volume)) + } + + /** + * Create a copy of the PadContent instance + * + * @param pad target pad + * @return copied content + */ + override def copy(pad: Pad): PadContent = { + val clone = new ContentPlayerPadContent(pad, getType) + clone.loadMedia() + clone + } + + /* + Custom Settings + */ + + def shouldShowLastFrame(): Boolean = { + pad.getPadSettings.getCustomSettings.getOrDefault(ContentPlayerPadContentFactory.lastFrame, false).asInstanceOf[Boolean] + } + + def getSelectedZones: Seq[Zone] = { + val zoneConfiguration = Profile.currentProfile().getCustomSettings(ContentPluginMain.zoneConfigurationKey).asInstanceOf[ZoneConfiguration] + + val customSettings = pad.getPadSettings.getCustomSettings + val selectedZoneNames = customSettings.getOrDefault( + ContentPlayerPadContentFactory.zones, + zoneConfiguration.zones.stream().map(zone => zone.getName).collect(Collectors.toList()) + ).asInstanceOf[util.List[String]] + zoneConfiguration.zones.asScala.filter(zone => selectedZoneNames.contains(zone.getName)).toSeq + } +} diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerPadContentFactory.scala b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerPadContentFactory.scala new file mode 100644 index 0000000000000000000000000000000000000000..d08958d5da016d424a9204a12317b8fbade30f93 --- /dev/null +++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerPadContentFactory.scala @@ -0,0 +1,25 @@ +package de.tobias.playpad.plugin.content.pad + +import de.tobias.playpad.pad.Pad +import de.tobias.playpad.pad.content.{PadContent, PadContentFactory} +import de.tobias.playpad.pad.view.IPadContentView +import de.tobias.playpad.viewcontroller.PadSettingsTabViewController +import javafx.scene.layout.Pane + +class ContentPlayerPadContentFactory(val `type`: String) extends PadContentFactory(`type`) { + + override def newInstance(pad: Pad): PadContent = new ContentPlayerPadContent(pad, getType) + + override def getPadContentPreview(pad: Pad, parentNode: Pane): IPadContentView = new ContentPlayerPadPreview(pad, parentNode) + + override def getSettingsViewController(pad: Pad): PadSettingsTabViewController = new ContentPlayerPadContentSettingsViewController(pad) + + override def getSupportedTypes: Array[String] = ContentPlayerPadContentFactory.FILE_EXTENSION +} + +object ContentPlayerPadContentFactory { + private val FILE_EXTENSION = Array("*.mp4", "*.mov") + + val lastFrame = "ContentLastFrame" + val zones = "zones" +} \ No newline at end of file diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerPadContentSettingsViewController.scala b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerPadContentSettingsViewController.scala new file mode 100644 index 0000000000000000000000000000000000000000..14d5e0ee477047de962dfd5959c2ee67d0a9c1c8 --- /dev/null +++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerPadContentSettingsViewController.scala @@ -0,0 +1,73 @@ +package de.tobias.playpad.plugin.content.pad + +import java.util + +import de.thecodelabs.utils.util.Localization +import de.tobias.playpad.pad.Pad +import de.tobias.playpad.plugin.content.ContentPluginMain +import de.tobias.playpad.plugin.content.settings.{Zone, ZoneConfiguration} +import de.tobias.playpad.profile.Profile +import de.tobias.playpad.viewcontroller.PadSettingsTabViewController +import javafx.beans.binding.Bindings +import javafx.fxml.FXML +import javafx.scene.control.{Button, CheckBox} +import org.controlsfx.control.CheckListView + +import scala.jdk.CollectionConverters._ + +class ContentPlayerPadContentSettingsViewController(val pad: Pad) extends PadSettingsTabViewController { + + @FXML + var lastFrameCheckbox: CheckBox = _ + @FXML + var zoneListView: CheckListView[Zone] = _ + + @FXML + var addAllZonesButton: Button = _ + @FXML + var removeAllZonesButton: Button = _ + + load("view", "ContentPadSettings", Localization.getBundle) + + override def init(): Unit = { + val zoneConfiguration = Profile.currentProfile().getCustomSettings(ContentPluginMain.zoneConfigurationKey).asInstanceOf[ZoneConfiguration] + zoneListView.getItems.addAll(zoneConfiguration.zones) + + addAllZonesButton.disableProperty().bind(Bindings.equal(Bindings.size(zoneListView.getCheckModel.getCheckedIndices), zoneListView.getItems.size())) + removeAllZonesButton.disableProperty().bind(Bindings.isEmpty(zoneListView.getCheckModel.getCheckedIndices)) + } + + override def getName: String = Localization.getString("plugin.content.player.settings") + + override def loadSettings(pad: Pad): Unit = { + val customSettings = pad.getPadSettings.getCustomSettings + val lastFrameOption = customSettings.get(ContentPlayerPadContentFactory.lastFrame) + if (lastFrameOption != null) { + lastFrameCheckbox.setSelected(lastFrameOption.toString.toBoolean) + } + + pad.getContent match { + case content: ContentPlayerPadContent => + content.getSelectedZones.foreach(item => zoneListView.getCheckModel.check(item)) + case _ => + } + } + + override def saveSettings(pad: Pad): Unit = { + val customSettings = pad.getPadSettings.getCustomSettings + customSettings.put(ContentPlayerPadContentFactory.lastFrame, lastFrameCheckbox.isSelected) + + val selectedZoneNames = zoneListView.getCheckModel.getCheckedItems.asScala.map(zone => zone.getName) + customSettings.put(ContentPlayerPadContentFactory.zones, new util.ArrayList(selectedZoneNames.asJavaCollection)) + } + + @FXML + def onAddAllZonesHandler(): Unit = { + zoneListView.getCheckModel.checkAll() + } + + @FXML + def onRemoveAllZonesHandler(): Unit = { + zoneListView.getCheckModel.clearChecks() + } +} diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerPadPreview.scala b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerPadPreview.scala new file mode 100644 index 0000000000000000000000000000000000000000..6ff298194c1ec57c2f6e7262b8c1c96db6498476 --- /dev/null +++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerPadPreview.scala @@ -0,0 +1,68 @@ +package de.tobias.playpad.plugin.content.pad + +import de.thecodelabs.utils.io.PathUtils +import de.tobias.playpad.pad.Pad +import de.tobias.playpad.pad.view.IPadContentView +import javafx.beans.binding.Bindings +import javafx.collections.ListChangeListener +import javafx.geometry.{Insets, Pos} +import javafx.scene.Parent +import javafx.scene.control.Label +import javafx.scene.layout.{Pane, Priority, VBox} +import javafx.scene.text.TextAlignment + +class ContentPlayerPadPreview(pad: Pad, parent: Pane) extends VBox with IPadContentView { + + val nameLabel: Label = new Label() + val subTitleLabel: Label = new Label() + + setupLabel(nameLabel) + setupLabel(subTitleLabel) + + getChildren.addAll(nameLabel, subTitleLabel) + setSpacing(3) + setPadding(new Insets(7)) + + getStyleClass.addListener(new ListChangeListener[String] { + override def onChanged(c: ListChangeListener.Change[_ <: String]): Unit = { + while (c.next()) { + if (c.wasRemoved()) { + nameLabel.getStyleClass.removeAll(c.getRemoved) + subTitleLabel.getStyleClass.removeAll(c.getRemoved) + } + + if (c.wasAdded()) { + nameLabel.getStyleClass.addAll(c.getAddedSubList) + subTitleLabel.getStyleClass.addAll(c.getAddedSubList) + } + } + } + }) + + nameLabel.textProperty.bind(pad.nameProperty) + pad.getContent match { + case content: ContentPlayerPadContent => + subTitleLabel.textProperty().bind(Bindings.createStringBinding(() => { + if (content.getCurrentPlayingMediaIndex < 0) "" else PathUtils.getFilenameWithoutExtension(pad.getPaths.get(content.getCurrentPlayingMediaIndex).getPath.getFileName) + }, content.currentPlayingMediaIndexProperty())) + case _ => + } + + private def setupLabel(label: Label): Unit = { + label.setWrapText(true) + label.setAlignment(Pos.CENTER) + label.setTextAlignment(TextAlignment.CENTER) + + label.prefWidthProperty.bind(parent.widthProperty) + label.setMaxHeight(Double.MaxValue) + + VBox.setVgrow(label, Priority.ALWAYS) + } + + override def getNode: Parent = this + + override def deInit(): Unit = { + nameLabel.textProperty().unbind() + subTitleLabel.textProperty().unbind() + } +} diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/player/ContentPlayerViewController.scala b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/player/ContentPlayerViewController.scala new file mode 100644 index 0000000000000000000000000000000000000000..c659e296cd880469fcae5e27ca2f5b193fad0258 --- /dev/null +++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/player/ContentPlayerViewController.scala @@ -0,0 +1,118 @@ +package de.tobias.playpad.plugin.content.player + +import de.thecodelabs.logger.Logger +import de.thecodelabs.utils.ui.size.IgnoreStageSizing +import de.thecodelabs.utils.ui.{NVC, NVCStage} +import de.tobias.playpad.PlayPadPlugin +import de.tobias.playpad.plugin.content.settings.{Zone, ZoneConfiguration} +import de.tobias.playpad.project.page.PadIndex +import javafx.geometry.Insets +import javafx.scene.layout._ +import javafx.scene.media.MediaPlayer +import javafx.scene.paint.Color +import javafx.stage.{Stage, StageStyle} + +import scala.collection.mutable.ListBuffer + +@IgnoreStageSizing +class ContentPlayerViewController extends NVC { + + private val mediaStacks: ListBuffer[MediaPlayerStack] = ListBuffer.empty + + load("view", "PlayerView") + private val stageContainer: NVCStage = applyViewControllerToStage + stageContainer.addCloseHook(() => false) + + Logger.debug("Create Player View Controller") + + override def init(): Unit = { + val parent = getParent.asInstanceOf[Pane] + parent.setBackground(new Background(new BackgroundFill(Color.BLACK, CornerRadii.EMPTY, Insets.EMPTY))) + } + + override def initStage(stage: Stage): Unit = { + super.initStage(stage) + stage.initStyle(StageStyle.UNDECORATED) + stage.setAlwaysOnTop(true) + + stage.getScene.setFill(Color.BLACK) + stage.getIcons.add(PlayPadPlugin.getInstance().getIcon) + } + + def showMediaPlayer(padIndex: PadIndex, mediaPlayer: MediaPlayer, zones: Seq[Zone]): Unit = { + val iterator = this.mediaStacks.iterator + while (iterator.hasNext) { + val mediaPlayerStack = iterator.next() + if (zones.contains(mediaPlayerStack.zone)) { + mediaPlayerStack.showMediaPlayer(padIndex, mediaPlayer) + } + } + } + + def disconnectMediaPlayer(mediaPlayer: MediaPlayer, zones: Seq[Zone]): Unit = { + val iterator = this.mediaStacks.iterator + while (iterator.hasNext) { + val mediaPlayerStack = iterator.next() + if (zones.contains(mediaPlayerStack.zone)) { + mediaPlayerStack.disconnectMediaPlayer(mediaPlayer) + } + } + } + + def configurePlayers(configuration: ZoneConfiguration): Unit = { + if (configuration.zones.isEmpty) { + closeStage() + return + } + + val parent = getParent.asInstanceOf[Pane] + parent.getChildren.clear() + + mediaStacks.clear() + configuration.zones.forEach(player => { + val mediaPlayerStack = new MediaPlayerStack(player) + mediaStacks.addOne(mediaPlayerStack) + parent.getChildren.add(mediaPlayerStack) + }) + + showStage() + + getStageContainer.ifPresent(container => { + val stage = container.getStage + + import scala.jdk.CollectionConverters._ + val zones = configuration.zones.asScala + val maxWidth = zones.map(player => player.x + player.width).max + val maxHeight = zones.map(player => player.y + player.height).max + + stage.setX(0) + stage.setY(0) + stage.setWidth(maxWidth) + stage.setHeight(maxHeight) + }) + } + + def addActivePadToList(padIndex: PadIndex, zones: Seq[Zone]): Unit = getMediaStacks(zones) + .foreach(mediaStack => mediaStack.addActivePad(padIndex)) + + def removeActivePadFromList(padIndex: PadIndex, zones: Seq[Zone]): Unit = getMediaStacks(zones) + .foreach(mediaStack => mediaStack.removeActivePad(padIndex)) + + def highlight(zone: Zone, on: Boolean): Unit = { + if (getMediaStack(zone).isEmpty) { + return + } + getMediaStack(zone).head.highlight(on) + } + + def setFadeValue(mediaPlayer: MediaPlayer, zones: Seq[Zone], value: Double): Unit = getMediaStacks(zones) + .foreach(mediaStack => mediaStack.setFadeValue(mediaPlayer, value)) + + private def getMediaStack(zone: Zone): ListBuffer[MediaPlayerStack] = { + getMediaStacks(List(zone)) + } + + private def getMediaStacks(zones: Seq[Zone]): ListBuffer[MediaPlayerStack] = { + mediaStacks.filter(mediaPlayer => zones.contains(mediaPlayer.zone)) + } +} diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/player/MediaPlayerStack.scala b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/player/MediaPlayerStack.scala new file mode 100644 index 0000000000000000000000000000000000000000..15d3872130e3fe4b1d32c3ff3d67f11329a70c6d --- /dev/null +++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/player/MediaPlayerStack.scala @@ -0,0 +1,78 @@ +package de.tobias.playpad.plugin.content.player + +import java.util.stream.Collectors + +import de.thecodelabs.logger.Logger +import de.tobias.playpad.plugin.content.settings.Zone +import de.tobias.playpad.project.page.PadIndex +import javafx.geometry.Insets +import javafx.scene.layout.{Background, BackgroundFill, CornerRadii, StackPane} +import javafx.scene.media.{MediaPlayer, MediaView} +import javafx.scene.paint.Color + +import scala.collection.mutable +import scala.collection.mutable.ListBuffer + +class MediaPlayerStack(val zone: Zone) extends StackPane { + + private var activePlayers: ListBuffer[PadIndex] = ListBuffer.empty + val mediaViews: mutable.Map[MediaPlayer, MediaView] = new mutable.HashMap[MediaPlayer, MediaView]() + + setLayoutX(zone.x) + setLayoutY(zone.y) + setMinWidth(zone.width) + setMaxWidth(zone.width) + setMinHeight(zone.height) + setMaxHeight(zone.height) + + def addActivePad(padIndex: PadIndex): Unit = activePlayers.addOne(padIndex) + + def removeActivePad(padIndex: PadIndex): Unit = activePlayers = activePlayers.filter(element => element != padIndex) + + def showMediaPlayer(padIndex: PadIndex, mediaPlayer: MediaPlayer): Unit = { + if (!mediaViews.contains(mediaPlayer)) { + val mediaView = new MediaView(mediaPlayer) + mediaView.setFitWidth(zone.width) + mediaView.setFitHeight(zone.height) + mediaViews.put(mediaPlayer, mediaView) + } + + val mediaView = mediaViews(mediaPlayer) + mediaView.setUserData(padIndex) + mediaView.setOpacity(1.0) + + if (!getChildren.contains(mediaView)) { + val index = activePlayers.indexOf(padIndex) + try { + getChildren.add(index, mediaView) + } catch { + case e: Exception => + Logger.error(e) + getChildren.add(mediaView) + } + } + } + + def disconnectMediaPlayer(mediaPlayer: MediaPlayer): Unit = { + if (mediaViews.contains(mediaPlayer)) { + getChildren.remove(mediaViews(mediaPlayer)) + } + } + + def setFadeValue(mediaPlayer: MediaPlayer, value: Double): Unit = { + if (mediaViews.contains(mediaPlayer)) { + val mediaView = mediaViews(mediaPlayer) + mediaView.setOpacity(value) + } + } + + def highlight(on: Boolean): Unit = { + if (on) { + setBackground(new Background(new BackgroundFill(Color.RED, CornerRadii.EMPTY, Insets.EMPTY))) + } else { + setBackground(null) + } + } + + override def toString: String = f"MediaPlayerStack: ${getChildren.stream().map(view => f"MediaView: ${view.getUserData}").collect(Collectors.joining(", "))}" +} diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/settings/ZoneConfiguration.scala b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/settings/ZoneConfiguration.scala new file mode 100644 index 0000000000000000000000000000000000000000..956a0183c9eafa8245a6565558bf85aeaa176e4c --- /dev/null +++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/settings/ZoneConfiguration.scala @@ -0,0 +1,43 @@ +package de.tobias.playpad.plugin.content.settings + +import java.util +import java.util.{List => JavaList} + +import de.thecodelabs.storage.settings.annotation.{FilePath, Key} +import de.tobias.playpad.Displayable +import javafx.beans.property.{SimpleStringProperty, StringProperty} + +@FilePath("players.json") +class ZoneConfiguration { + @Key + var zones: JavaList[Zone] = new util.ArrayList[Zone]() +} + +class Zone extends Displayable { + @Key + private var name: String = _ + @Key + var x: Double = _ + @Key + var y: Double = _ + @Key + var width: Double = _ + @Key + var height: Double = _ + + def getName: String = name + + def setName(name: String): Unit = { + this.name = name + _displayProperty.set(name) + } + + private val _displayProperty: StringProperty = new SimpleStringProperty(name) + + override def displayProperty(): StringProperty = { + _displayProperty.set(name) + _displayProperty + } + + override def toString: String = name +} diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/settings/ZoneSettingsViewController.scala b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/settings/ZoneSettingsViewController.scala new file mode 100644 index 0000000000000000000000000000000000000000..bae668ff818ef49c8b463df091ccea02bffdb6f4 --- /dev/null +++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/settings/ZoneSettingsViewController.scala @@ -0,0 +1,134 @@ +package de.tobias.playpad.plugin.content.settings + +import de.thecodelabs.utils.ui.scene.input.NumberTextField +import de.thecodelabs.utils.util.Localization +import de.tobias.playpad.plugin.content.ContentPluginMain +import de.tobias.playpad.profile.{Profile, ProfileSettings} +import de.tobias.playpad.project.Project +import de.tobias.playpad.viewcontroller.main.IMainViewController +import de.tobias.playpad.viewcontroller.option.{IProfileReloadTask, ProfileSettingsTabViewController} +import javafx.application.Platform +import javafx.fxml.FXML +import javafx.scene.control.{Button, ListCell, ListView, TextField} + +class ZoneSettingsViewController extends ProfileSettingsTabViewController with IProfileReloadTask { + + @FXML + var listView: ListView[Zone] = _ + + @FXML + var nameTextField: TextField = _ + @FXML + var xTextField: NumberTextField = _ + @FXML + var yTextField: NumberTextField = _ + @FXML + var widthTextField: NumberTextField = _ + @FXML + var heightTextField: NumberTextField = _ + + @FXML + var addButton: Button = _ + @FXML + var removeButton: Button = _ + + load("view", "ZoneSettings", Localization.getBundle) + + override def init(): Unit = { + listView.setCellFactory((_: ListView[Zone]) => new ListCell[Zone] { + override def updateItem(item: Zone, empty: Boolean): Unit = { + super.updateItem(item, empty) + if (!empty) { + textProperty().bind(item.displayProperty()) + } else { + textProperty().unbind() + setText("") + } + } + }) + listView.getSelectionModel.selectedItemProperty().addListener((_, oldValue, newValue) => { + val playerViewController = ContentPluginMain.playerViewController + + if (oldValue != null) { + saveSettingsToZone(oldValue) + playerViewController.highlight(oldValue, on = false) + } + if (newValue != null) { + showSettingsOfZone(newValue) + playerViewController.highlight(newValue, on = true) + } else { + clearTextFields() + } + }) + } + + private def saveSettingsToZone(zone: Zone): Unit = { + zone.setName(nameTextField.getText) + zone.x = xTextField.getText.toDouble + zone.y = yTextField.getText.toDouble + zone.width = widthTextField.getText.toDouble + zone.height = heightTextField.getText.toDouble + } + + private def showSettingsOfZone(zone: Zone): Unit = { + nameTextField.setText(zone.getName) + xTextField.setText(zone.x.toInt.toString) + yTextField.setText(zone.y.toInt.toString) + widthTextField.setText(zone.width.toInt.toString) + heightTextField.setText(zone.height.toInt.toString) + } + + private def clearTextFields(): Unit = { + nameTextField.setText("") + xTextField.setText("") + yTextField.setText("") + widthTextField.setText("") + heightTextField.setText("") + } + + // Actions + @FXML + def onAddHandle(): Unit = { + val newConfiguration = new Zone + newConfiguration.setName(Localization.getString("plugin.content.player.settings.default_name")) + + getZoneConfiguration.zones.add(newConfiguration) + listView.getItems.add(newConfiguration) + } + + @FXML + def onRemoveHandle(): Unit = { + val selectedItem = listView.getSelectionModel.getSelectedItem + if (selectedItem != null) { + listView.getItems.remove(selectedItem) + getZoneConfiguration.zones.remove(selectedItem) + } + } + + override def loadSettings(settings: Profile): Unit = { + listView.getItems.setAll(getZoneConfiguration.zones) + } + + override def saveSettings(settings: Profile): Unit = { + val selectedItem = listView.getSelectionModel.getSelectedItem + if (selectedItem != null) { + saveSettingsToZone(selectedItem) + } + } + + override def needReload(): Boolean = { + true + } + + override def validSettings(): Boolean = { + true + } + + override def name(): String = Localization.getString("plugin.content.player.settings") + + + override def getTask(settings: ProfileSettings, project: Project, controller: IMainViewController): Runnable = () => + Platform.runLater(() => ContentPluginMain.playerViewController.configurePlayers(getZoneConfiguration)) + + private def getZoneConfiguration: ZoneConfiguration = Profile.currentProfile().getCustomSettings(ContentPluginMain.zoneConfigurationKey).asInstanceOf[ZoneConfiguration] +} diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/util/package.scala b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/util/package.scala new file mode 100644 index 0000000000000000000000000000000000000000..22836f6b9326accde9478b56a7e65861ff3e5fb3 --- /dev/null +++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/util/package.scala @@ -0,0 +1,31 @@ +package de.tobias.playpad.plugin.content + +import javafx.collections.ObservableList + +package object util { + + implicit class ObservableListExtension[E >: Null](list: ObservableList[E]) { + def head: E = { + if (list.isEmpty) { + return null + } + + list.get(0) + } + + def apply(index: Int): E = list.get(index) + + def length: Long = list.size() + + def isNotEmpty: Boolean = !list.isEmpty + + def indexWhere(predicate: E => Boolean): Int = { + for (i <- 0 until list.size()) { + if (predicate(list.get(i))) { + return i + } + } + -1 + } + } +} diff --git a/PlayWallPlugins/PlayWallPluginMedia/src/main/java/de/tobias/playpad/plugin/media/image/ImagePadContentFactory.java b/PlayWallPlugins/PlayWallPluginMedia/src/main/java/de/tobias/playpad/plugin/media/image/ImagePadContentFactory.java index 4a9a5ec7b072aa401cb87eea1093050a199b6539..d9c10d492f3725b1bd743b09aab267bfe581046f 100644 --- a/PlayWallPlugins/PlayWallPluginMedia/src/main/java/de/tobias/playpad/plugin/media/image/ImagePadContentFactory.java +++ b/PlayWallPlugins/PlayWallPluginMedia/src/main/java/de/tobias/playpad/plugin/media/image/ImagePadContentFactory.java @@ -9,7 +9,7 @@ import javafx.collections.ListChangeListener; import javafx.collections.SetChangeListener; import javafx.css.PseudoClass; import javafx.geometry.Pos; -import javafx.scene.Node; +import javafx.scene.Parent; import javafx.scene.control.Label; import javafx.scene.layout.Pane; import javafx.scene.layout.Priority; @@ -91,7 +91,7 @@ public class ImagePadContentFactory extends PadContentFactory { } @Override - public Node getNode() { + public Parent getNode() { return stackPane; } diff --git a/PlayWallPlugins/PlayWallPluginMedia/src/main/java/de/tobias/playpad/plugin/media/video/VideoPadContentFactory.java b/PlayWallPlugins/PlayWallPluginMedia/src/main/java/de/tobias/playpad/plugin/media/video/VideoPadContentFactory.java index 96ccd6f3052a05e4ceccc974c486a0708920d2e9..14e2269f4a819ec63df0519285189c2c69be1cae 100644 --- a/PlayWallPlugins/PlayWallPluginMedia/src/main/java/de/tobias/playpad/plugin/media/video/VideoPadContentFactory.java +++ b/PlayWallPlugins/PlayWallPluginMedia/src/main/java/de/tobias/playpad/plugin/media/video/VideoPadContentFactory.java @@ -3,18 +3,13 @@ package de.tobias.playpad.plugin.media.video; import de.tobias.playpad.pad.Pad; import de.tobias.playpad.pad.content.PadContent; import de.tobias.playpad.pad.content.PadContentFactory; +import de.tobias.playpad.pad.preview.PadTextPreview; import de.tobias.playpad.pad.view.IPadContentView; import de.tobias.playpad.plugin.media.main.impl.MediaPluginImpl; import de.tobias.playpad.plugin.media.main.impl.MediaSettingsTabViewController; import de.tobias.playpad.viewcontroller.PadSettingsTabViewController; import de.tobias.playpad.viewcontroller.option.ProfileSettingsTabViewController; -import javafx.geometry.Pos; -import javafx.scene.Node; -import javafx.scene.control.Label; import javafx.scene.layout.Pane; -import javafx.scene.layout.Priority; -import javafx.scene.layout.VBox; -import javafx.scene.text.TextAlignment; public class VideoPadContentFactory extends PadContentFactory { @@ -31,7 +26,7 @@ public class VideoPadContentFactory extends PadContentFactory { @Override public IPadContentView getPadContentPreview(Pad pad, Pane parentNode) { - return new VideoContentView(pad, parentNode); + return new PadTextPreview(pad, parentNode); } @Override @@ -50,31 +45,4 @@ public class VideoPadContentFactory extends PadContentFactory { return FILE_EXTENSION; } - private static class VideoContentView implements IPadContentView { - - private final Label nameLabel; - - VideoContentView(Pad pad, Pane parentNode) { - nameLabel = new Label(); - nameLabel.textProperty().bind(pad.nameProperty()); - - nameLabel.setWrapText(true); - nameLabel.setAlignment(Pos.CENTER); - nameLabel.setTextAlignment(TextAlignment.CENTER); - - nameLabel.prefWidthProperty().bind(parentNode.widthProperty()); - nameLabel.setMaxHeight(Double.MAX_VALUE); - VBox.setVgrow(nameLabel, Priority.ALWAYS); - } - - @Override - public Node getNode() { - return nameLabel; - } - - @Override - public void deInit() { - nameLabel.textProperty().unbind(); - } - } } diff --git a/PlayWallPlugins/pom.xml b/PlayWallPlugins/pom.xml index e633d8b643444f5319297ee50360e08d1722198a..eda2b0ff82e4742bc5850d09101eb204099dc80a 100644 --- a/PlayWallPlugins/pom.xml +++ b/PlayWallPlugins/pom.xml @@ -20,6 +20,7 @@ <module>PlayWallPluginNativeAudio</module> <module>PlayWallPluginPlayoutLog</module> <module>PlayWallPluginWebAPI</module> + <module>PlayWallPluginContentPlayer</module> </modules> </project> \ No newline at end of file