From 84c9a23fa93bf52a3293e86c13977e756dde8edf Mon Sep 17 00:00:00 2001 From: mkpaz Date: Tue, 26 Jul 2022 22:45:58 +0400 Subject: [PATCH] Add showcases --- pom.xml | 10 + sampler/pom.xml | 8 + .../sampler/layout/ApplicationWindow.java | 5 + .../atlantafx/sampler/layout/Sidebar.java | 11 +- .../atlantafx/sampler/page/showcase/Chat.java | 22 - .../sampler/page/showcase/FileManager.java | 22 - .../sampler/page/showcase/MusicPlayer.java | 22 - .../sampler/page/showcase/ShowcasePage.java | 78 +++ .../sampler/page/showcase/TextEditor.java | 22 - .../page/showcase/filemanager/Bookmark.java | 8 + .../showcase/filemanager/BookmarkList.java | 76 +++ .../showcase/filemanager/DirectoryView.java | 18 + .../page/showcase/filemanager/FileList.java | 57 ++ .../showcase/filemanager/FileManagerPage.java | 168 +++++ .../page/showcase/filemanager/Model.java | 47 ++ .../filemanager/NavigationHistory.java | 50 ++ .../filemanager/TableDirectoryView.java | 185 ++++++ .../page/showcase/filemanager/Utils.java | 62 ++ .../showcase/filemanager/file-manager.css | 27 + .../page/showcase/musicplayer/ColorThief.java | 583 ++++++++++++++++++ .../page/showcase/musicplayer/MediaFile.java | 70 +++ .../page/showcase/musicplayer/Model.java | 89 +++ .../showcase/musicplayer/MusicPlayerPage.java | 78 +++ .../page/showcase/musicplayer/Player.java | 262 ++++++++ .../page/showcase/musicplayer/Playlist.java | 179 ++++++ .../page/showcase/musicplayer/Utils.java | 48 ++ .../showcase/musicplayer/music-player.css | 26 + sampler/src/main/java/module-info.java | 2 + .../main/resources/assets/styles/index.css | 15 + .../src/main/resources/images/no-image.png | Bin 0 -> 21222 bytes sampler/src/main/resources/images/vinyl.jpg | Bin 0 -> 95946 bytes styles/src/components/_data.scss | 20 +- styles/src/components/_menu-button.scss | 3 + styles/src/general/_root.scss | 1 + 34 files changed, 2178 insertions(+), 96 deletions(-) delete mode 100644 sampler/src/main/java/atlantafx/sampler/page/showcase/Chat.java delete mode 100644 sampler/src/main/java/atlantafx/sampler/page/showcase/FileManager.java delete mode 100644 sampler/src/main/java/atlantafx/sampler/page/showcase/MusicPlayer.java create mode 100644 sampler/src/main/java/atlantafx/sampler/page/showcase/ShowcasePage.java delete mode 100644 sampler/src/main/java/atlantafx/sampler/page/showcase/TextEditor.java create mode 100644 sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/Bookmark.java create mode 100644 sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/BookmarkList.java create mode 100644 sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/DirectoryView.java create mode 100644 sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/FileList.java create mode 100644 sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/FileManagerPage.java create mode 100644 sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/Model.java create mode 100644 sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/NavigationHistory.java create mode 100644 sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/TableDirectoryView.java create mode 100644 sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/Utils.java create mode 100644 sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/file-manager.css create mode 100644 sampler/src/main/java/atlantafx/sampler/page/showcase/musicplayer/ColorThief.java create mode 100644 sampler/src/main/java/atlantafx/sampler/page/showcase/musicplayer/MediaFile.java create mode 100644 sampler/src/main/java/atlantafx/sampler/page/showcase/musicplayer/Model.java create mode 100644 sampler/src/main/java/atlantafx/sampler/page/showcase/musicplayer/MusicPlayerPage.java create mode 100644 sampler/src/main/java/atlantafx/sampler/page/showcase/musicplayer/Player.java create mode 100644 sampler/src/main/java/atlantafx/sampler/page/showcase/musicplayer/Playlist.java create mode 100644 sampler/src/main/java/atlantafx/sampler/page/showcase/musicplayer/Utils.java create mode 100644 sampler/src/main/java/atlantafx/sampler/page/showcase/musicplayer/music-player.css create mode 100644 sampler/src/main/resources/images/no-image.png create mode 100644 sampler/src/main/resources/images/vinyl.jpg diff --git a/pom.xml b/pom.xml index 37b472d..a2eb129 100755 --- a/pom.xml +++ b/pom.xml @@ -99,6 +99,11 @@ javafx-swing ${openjfx.version} + + org.openjfx + javafx-media + ${openjfx.version} + org.openjfx javafx-web @@ -114,6 +119,11 @@ ikonli-feather-pack ${lib.ikonli.version} + + org.kordamp.ikonli + ikonli-material2-pack + ${lib.ikonli.version} + fr.brouillard.oss diff --git a/sampler/pom.xml b/sampler/pom.xml index c13495c..b5f5288 100755 --- a/sampler/pom.xml +++ b/sampler/pom.xml @@ -46,6 +46,10 @@ org.openjfx javafx-swing + + org.openjfx + javafx-media + org.openjfx javafx-web @@ -58,6 +62,10 @@ org.kordamp.ikonli ikonli-feather-pack + + org.kordamp.ikonli + ikonli-material2-pack + fr.brouillard.oss cssfx diff --git a/sampler/src/main/java/atlantafx/sampler/layout/ApplicationWindow.java b/sampler/src/main/java/atlantafx/sampler/layout/ApplicationWindow.java index 7f6bfef..a32dfcb 100755 --- a/sampler/src/main/java/atlantafx/sampler/layout/ApplicationWindow.java +++ b/sampler/src/main/java/atlantafx/sampler/layout/ApplicationWindow.java @@ -21,6 +21,11 @@ public class ApplicationWindow extends BorderPane { sidebar.setOnSelect(pageClass -> { try { + // reset previous page, e.g. to free resources + if (!pageContainer.getChildren().isEmpty() && pageContainer.getChildren().get(0) instanceof Page page) { + page.reset(); + } + Page page = pageClass.getDeclaredConstructor().newInstance(); pageContainer.getChildren().setAll(page.getView()); } catch (Exception e) { diff --git a/sampler/src/main/java/atlantafx/sampler/layout/Sidebar.java b/sampler/src/main/java/atlantafx/sampler/layout/Sidebar.java index d1014b4..beb5c51 100644 --- a/sampler/src/main/java/atlantafx/sampler/layout/Sidebar.java +++ b/sampler/src/main/java/atlantafx/sampler/layout/Sidebar.java @@ -7,6 +7,8 @@ import atlantafx.sampler.page.Page; import atlantafx.sampler.page.components.*; import atlantafx.sampler.page.general.ThemePage; import atlantafx.sampler.page.general.TypographyPage; +import atlantafx.sampler.page.showcase.filemanager.FileManagerPage; +import atlantafx.sampler.page.showcase.musicplayer.MusicPlayerPage; import atlantafx.sampler.util.Containers; import javafx.beans.binding.Bindings; import javafx.beans.property.ReadOnlyObjectWrapper; @@ -67,8 +69,8 @@ public class Sidebar extends VBox { var navScroll = new ScrollPane(navContainer); Containers.setScrollConstraints(navScroll, - ScrollPane.ScrollBarPolicy.AS_NEEDED, true, - ScrollPane.ScrollBarPolicy.AS_NEEDED, true + ScrollPane.ScrollBarPolicy.AS_NEEDED, true, + ScrollPane.ScrollBarPolicy.AS_NEEDED, true ); VBox.setVgrow(navScroll, ALWAYS); @@ -144,7 +146,10 @@ public class Sidebar extends VBox { navLink(ToolBarPage.NAME, ToolBarPage.class), navLink(TooltipPage.NAME, TooltipPage.class), navLink(TreePage.NAME, TreePage.class), - navLink(TreeTablePage.NAME, TreeTablePage.class) + navLink(TreeTablePage.NAME, TreeTablePage.class), + caption("SHOWCASE"), + navLink(FileManagerPage.NAME, FileManagerPage.class), + navLink(MusicPlayerPage.NAME, MusicPlayerPage.class) )); } diff --git a/sampler/src/main/java/atlantafx/sampler/page/showcase/Chat.java b/sampler/src/main/java/atlantafx/sampler/page/showcase/Chat.java deleted file mode 100644 index 27a8bb1..0000000 --- a/sampler/src/main/java/atlantafx/sampler/page/showcase/Chat.java +++ /dev/null @@ -1,22 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -package atlantafx.sampler.page.showcase; - -import atlantafx.sampler.page.AbstractPage; - -public class Chat extends AbstractPage { - - public static final String NAME = "Chat"; - - public Chat() { - super(); - createView(); - } - - private void createView() { - } - - @Override - public String getName() { - return NAME; - } -} diff --git a/sampler/src/main/java/atlantafx/sampler/page/showcase/FileManager.java b/sampler/src/main/java/atlantafx/sampler/page/showcase/FileManager.java deleted file mode 100644 index cd50dff..0000000 --- a/sampler/src/main/java/atlantafx/sampler/page/showcase/FileManager.java +++ /dev/null @@ -1,22 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -package atlantafx.sampler.page.showcase; - -import atlantafx.sampler.page.AbstractPage; - -public class FileManager extends AbstractPage { - - public static final String NAME = "File Manager"; - - public FileManager() { - super(); - createView(); - } - - private void createView() { - } - - @Override - public String getName() { - return NAME; - } -} diff --git a/sampler/src/main/java/atlantafx/sampler/page/showcase/MusicPlayer.java b/sampler/src/main/java/atlantafx/sampler/page/showcase/MusicPlayer.java deleted file mode 100644 index bb510b8..0000000 --- a/sampler/src/main/java/atlantafx/sampler/page/showcase/MusicPlayer.java +++ /dev/null @@ -1,22 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -package atlantafx.sampler.page.showcase; - -import atlantafx.sampler.page.AbstractPage; - -public class MusicPlayer extends AbstractPage { - - public static final String NAME = "Music Player"; - - public MusicPlayer() { - super(); - createView(); - } - - private void createView() { - } - - @Override - public String getName() { - return NAME; - } -} diff --git a/sampler/src/main/java/atlantafx/sampler/page/showcase/ShowcasePage.java b/sampler/src/main/java/atlantafx/sampler/page/showcase/ShowcasePage.java new file mode 100644 index 0000000..7fb1efa --- /dev/null +++ b/sampler/src/main/java/atlantafx/sampler/page/showcase/ShowcasePage.java @@ -0,0 +1,78 @@ +package atlantafx.sampler.page.showcase; + +import atlantafx.base.controls.Spacer; +import atlantafx.sampler.page.AbstractPage; +import javafx.css.PseudoClass; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.control.Alert; +import javafx.scene.control.Button; +import javafx.scene.control.Hyperlink; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; +import javafx.scene.layout.VBox; +import javafx.stage.StageStyle; +import org.kordamp.ikonli.feather.Feather; +import org.kordamp.ikonli.javafx.FontIcon; + +import static atlantafx.base.theme.Styles.ACCENT; + +public abstract class ShowcasePage extends AbstractPage { + + protected static final PseudoClass SHOWCASE_MODE = PseudoClass.getPseudoClass("showcase-mode"); + protected final AnchorPane showcase = new AnchorPane(); + protected final HBox expandBox = new HBox(10); + protected final HBox collapseBox = new HBox(10); + + public ShowcasePage() { + createShowcaseLayout(); + } + + protected void createShowcaseLayout() { + var expandBtn = new Button("Expand"); + expandBtn.setGraphic(new FontIcon(Feather.MAXIMIZE_2)); + expandBtn.getStyleClass().add(ACCENT); + expandBtn.setOnAction(e -> { + expandBtn.getScene().getRoot().pseudoClassStateChanged(SHOWCASE_MODE, true); + VBox.setVgrow(showcase, Priority.ALWAYS); + expandBox.setVisible(false); + expandBox.setManaged(false); + collapseBox.setVisible(true); + collapseBox.setManaged(true); + }); + expandBox.getChildren().setAll(new Spacer(), expandBtn, new Spacer()); + expandBox.setAlignment(Pos.CENTER_LEFT); + + var collapseBtn = new Hyperlink("Exit showcase mode"); + collapseBtn.setOnAction(e -> { + expandBtn.getScene().getRoot().pseudoClassStateChanged(SHOWCASE_MODE, false); + VBox.setVgrow(showcase, Priority.NEVER); + expandBox.setVisible(true); + expandBox.setManaged(true); + collapseBox.setVisible(false); + collapseBox.setManaged(false); + }); + collapseBox.getChildren().setAll(new FontIcon(Feather.MINIMIZE_2), collapseBtn); + collapseBox.setAlignment(Pos.CENTER_LEFT); + collapseBox.setPadding(new Insets(5)); + collapseBox.setVisible(false); + collapseBox.setManaged(false); + + sourceCodeToggleBtn.setVisible(false); + sourceCodeToggleBtn.setManaged(false); + + userContent.getChildren().setAll(showcase, expandBox, collapseBox); + } + + @SuppressWarnings("SameParameterValue") + protected void showWarning(String header, String description) { + var alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("Error Dialog"); + alert.setHeaderText(header); + alert.setContentText(description); + alert.initOwner(getScene().getWindow()); + alert.initStyle(StageStyle.DECORATED); + alert.showAndWait(); + } +} diff --git a/sampler/src/main/java/atlantafx/sampler/page/showcase/TextEditor.java b/sampler/src/main/java/atlantafx/sampler/page/showcase/TextEditor.java deleted file mode 100644 index 90a1e48..0000000 --- a/sampler/src/main/java/atlantafx/sampler/page/showcase/TextEditor.java +++ /dev/null @@ -1,22 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -package atlantafx.sampler.page.showcase; - -import atlantafx.sampler.page.AbstractPage; - -public class TextEditor extends AbstractPage { - - public static final String NAME = "Text Editor"; - - public TextEditor() { - super(); - createView(); - } - - private void createView() { - } - - @Override - public String getName() { - return NAME; - } -} diff --git a/sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/Bookmark.java b/sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/Bookmark.java new file mode 100644 index 0000000..c720285 --- /dev/null +++ b/sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/Bookmark.java @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: MIT */ +package atlantafx.sampler.page.showcase.filemanager; + +import org.kordamp.ikonli.Ikon; + +import java.nio.file.Path; + +record Bookmark(String title, Path path, Ikon icon) { } diff --git a/sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/BookmarkList.java b/sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/BookmarkList.java new file mode 100644 index 0000000..02ed72b --- /dev/null +++ b/sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/BookmarkList.java @@ -0,0 +1,76 @@ +package atlantafx.sampler.page.showcase.filemanager; + +import javafx.scene.control.Alert; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.input.MouseEvent; +import org.kordamp.ikonli.feather.Feather; +import org.kordamp.ikonli.javafx.FontIcon; + +import java.nio.file.Files; +import java.nio.file.Path; + +import static atlantafx.sampler.page.showcase.filemanager.Model.USER_HOME; + +final class BookmarkList extends ListView { + + public BookmarkList(Model model) { + getStyleClass().add("bookmark-list"); + + // this is Linux specific and only for EN locale + getItems().setAll( + new Bookmark("Home", USER_HOME, Feather.HOME), + new Bookmark("Documents", USER_HOME.resolve("Documents"), Feather.FILE), + new Bookmark("Downloads", USER_HOME.resolve("Downloads"), Feather.DOWNLOAD), + new Bookmark("Music", USER_HOME.resolve("Music"), Feather.MUSIC), + new Bookmark("Pictures", USER_HOME.resolve("Pictures"), Feather.IMAGE), + new Bookmark("Videos", USER_HOME.resolve("Videos"), Feather.VIDEO) + ); + + setCellFactory(param -> { + var cell = new BookmarkCell(); + cell.setOnMousePressed((MouseEvent event) -> { + if (cell.isEmpty() || cell.getItem() == null) { + event.consume(); + } else { + Path path = cell.getItem().path(); + + if (!Files.exists(path)) { + var alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("Error Dialog"); + alert.setHeaderText("Sorry, this is just demo."); + alert.setContentText("There's no such directory as \"" + path + "\""); + alert.initOwner(getScene().getWindow()); + alert.showAndWait(); + return; + } + + model.navigate(path, true); + } + }); + return cell; + }); + } + + /////////////////////////////////////////////////////////////////////////// + + private static class BookmarkCell extends ListCell { + + public BookmarkCell() { + setGraphicTextGap(10); + } + + @Override + protected void updateItem(Bookmark bookmark, boolean empty) { + super.updateItem(bookmark, empty); + + if (empty) { + setGraphic(null); + setText(null); + } else { + setGraphic(new FontIcon(bookmark.icon())); + setText(bookmark.title()); + } + } + } +} diff --git a/sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/DirectoryView.java b/sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/DirectoryView.java new file mode 100644 index 0000000..7ceabae --- /dev/null +++ b/sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/DirectoryView.java @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: MIT */ +package atlantafx.sampler.page.showcase.filemanager; + +import javafx.scene.layout.Pane; + +import java.nio.file.Path; +import java.util.function.Consumer; + +interface DirectoryView { + + Pane getView(); + + FileList getFileList(); + + void setDirectory(Path path); + + void setOnAction(Consumer handler); +} diff --git a/sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/FileList.java b/sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/FileList.java new file mode 100644 index 0000000..8318001 --- /dev/null +++ b/sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/FileList.java @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: MIT */ +package atlantafx.sampler.page.showcase.filemanager; + +import javafx.beans.binding.Bindings; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.collections.transformation.FilteredList; +import javafx.collections.transformation.SortedList; +import javafx.scene.control.TableView; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Comparator; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static atlantafx.sampler.page.showcase.filemanager.Utils.isFileHidden; + +final class FileList { + + static final Comparator FILE_TYPE_COMPARATOR = + Comparator.comparing(path -> !Files.isDirectory(path)); + static final Predicate PREDICATE_ANY = path -> true; + static final Predicate PREDICATE_NOT_HIDDEN = path -> !isFileHidden(path); + + private final ObservableList list = FXCollections.observableArrayList(); + private final ObjectProperty> predicateProperty = new SimpleObjectProperty<>(path -> true); + + public FileList(TableView table) { + var filteredList = new FilteredList<>(list); + filteredList.predicateProperty().bind(predicateProperty); + + var sortedList = new SortedList<>(filteredList); + sortedList.comparatorProperty().bind(Bindings.createObjectBinding(() -> { + Comparator tableComparator = table.comparatorProperty().get(); + return tableComparator != null ? + FILE_TYPE_COMPARATOR.thenComparing(tableComparator) : + FILE_TYPE_COMPARATOR; + }, table.comparatorProperty())); + table.setItems(sortedList); + } + + public void load(Stream stream) { + list.setAll(stream.collect(Collectors.toList())); + } + + public void clear() { + list.clear(); + } + + public ObjectProperty> predicateProperty() { + return predicateProperty; + } +} diff --git a/sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/FileManagerPage.java b/sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/FileManagerPage.java new file mode 100644 index 0000000..6881159 --- /dev/null +++ b/sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/FileManagerPage.java @@ -0,0 +1,168 @@ +/* SPDX-License-Identifier: MIT */ +package atlantafx.sampler.page.showcase.filemanager; + +import atlantafx.base.controls.Breadcrumbs; +import atlantafx.base.controls.Spacer; +import atlantafx.base.theme.Styles; +import atlantafx.sampler.page.showcase.ShowcasePage; +import atlantafx.sampler.util.Containers; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.control.*; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import javafx.util.Callback; +import org.kordamp.ikonli.feather.Feather; +import org.kordamp.ikonli.javafx.FontIcon; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import static atlantafx.base.theme.Styles.*; +import static atlantafx.sampler.page.showcase.filemanager.FileList.PREDICATE_ANY; +import static atlantafx.sampler.page.showcase.filemanager.FileList.PREDICATE_NOT_HIDDEN; +import static atlantafx.sampler.page.showcase.filemanager.Utils.openFile; + +public class FileManagerPage extends ShowcasePage { + + public static final String NAME = "File Manager"; + + private static final String STYLESHEET_URL = + Objects.requireNonNull(FileManagerPage.class.getResource("file-manager.css")).toExternalForm(); + + @Override + public String getName() { return NAME; } + + private final Model model = new Model(); + + public FileManagerPage() { + super(); + createView(); + } + + private void createView() { + var topBar = new ToolBar(); + + var backBtn = new Button("", new FontIcon(Feather.ARROW_LEFT)); + backBtn.getStyleClass().addAll(BUTTON_ICON, LEFT_PILL); + backBtn.setOnAction(e -> model.back()); + backBtn.disableProperty().bind(model.getHistory().canGoBackProperty().not()); + + var forthBtn = new Button("", new FontIcon(Feather.ARROW_RIGHT)); + forthBtn.getStyleClass().addAll(BUTTON_ICON, RIGHT_PILL); + forthBtn.setOnAction(e -> model.forth()); + forthBtn.disableProperty().bind(model.getHistory().canGoForthProperty().not()); + + var backForthBox = new HBox(backBtn, forthBtn); + backForthBox.setAlignment(Pos.CENTER_LEFT); + + Breadcrumbs breadcrumbs = breadCrumbs(); + breadcrumbs.setOnCrumbAction(e -> model.navigate(e.getSelectedCrumb().getValue(), true)); + + var toggleHiddenCheck = new CheckMenuItem("Show Hidden Files"); + toggleHiddenCheck.setSelected(true); + + var menuBtn = new MenuButton(); + menuBtn.setGraphic(new FontIcon(Feather.MORE_HORIZONTAL)); + menuBtn.getItems().setAll( + toggleHiddenCheck + ); + menuBtn.getStyleClass().add(BUTTON_ICON); + + topBar.getItems().setAll( + backForthBox, + breadcrumbs, + new Spacer(), + menuBtn + ); + + // ~ + + BookmarkList bookmarksList = new BookmarkList(model); + + DirectoryView directoryView = new TableDirectoryView(); + directoryView.setDirectory(model.currentPathProperty().get()); + directoryView.setOnAction(path -> { + if (Files.isDirectory(path)) { + model.navigate(path, true); + } else { + openFile(path); + } + }); + + // ~ + + var splitPane = new SplitPane(bookmarksList, directoryView.getView()); + splitPane.setDividerPositions(0.2, 0.8); + + var root = new BorderPane(); + root.getStyleClass().add("file-manager-showcase"); + root.getStylesheets().add(STYLESHEET_URL); + root.setTop(topBar); + root.setCenter(splitPane); + + toggleHiddenCheck.selectedProperty().addListener((obs, old, val) -> directoryView.getFileList() + .predicateProperty() + .set(val ? PREDICATE_ANY : PREDICATE_NOT_HIDDEN) + ); + + model.currentPathProperty().addListener((obs, old, val) -> { + if (!Files.isReadable(val)) { + showWarning("Access Denied", "You have no permission to enter \"" + val + "\""); + return; + } + + breadcrumbs.setSelectedCrumb( + Breadcrumbs.buildTreeModel(getParentPath(val, 5).toArray(Path[]::new)) + ); + directoryView.setDirectory(val); + }); + + showcase.getChildren().setAll(root); + Containers.setAnchors(root, Insets.EMPTY); + } + + private Breadcrumbs breadCrumbs() { + Callback, Button> crumbFactory = crumb -> { + var btn = new Button(crumb.getValue().getFileName().toString()); + btn.getStyleClass().add(Styles.FLAT); + btn.setFocusTraversable(false); + if (!crumb.getChildren().isEmpty()) { + btn.setGraphic(new FontIcon(Feather.CHEVRON_RIGHT)); + } + btn.setContentDisplay(ContentDisplay.RIGHT); + btn.setStyle("-fx-padding: 6px 12px 6px 12px;"); + return btn; + }; + + var breadcrumbs = new Breadcrumbs(); + breadcrumbs.setAutoNavigationEnabled(false); + breadcrumbs.setCrumbFactory(crumbFactory); + breadcrumbs.setSelectedCrumb( + Breadcrumbs.buildTreeModel(getParentPath(model.currentPathProperty().get(), 5).toArray(Path[]::new)) + ); + breadcrumbs.setMaxWidth(Region.USE_PREF_SIZE); + breadcrumbs.setMaxHeight(Region.USE_PREF_SIZE); + + return breadcrumbs; + } + + @SuppressWarnings("SameParameterValue") + private List getParentPath(Path path, int limit) { + var result = new ArrayList(); + + var cursor = path; + while (result.size() < limit && cursor.getParent() != null) { + result.add(cursor); + cursor = cursor.getParent(); + } + Collections.reverse(result); + + return result; + } +} diff --git a/sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/Model.java b/sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/Model.java new file mode 100644 index 0000000..be7d614 --- /dev/null +++ b/sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/Model.java @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: MIT */ +package atlantafx.sampler.page.showcase.filemanager; + +import javafx.beans.property.ReadOnlyObjectProperty; +import javafx.beans.property.ReadOnlyObjectWrapper; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Objects; + +final class Model { + + public static final Path USER_HOME = Paths.get(System.getProperty("user.home")); + + private final ReadOnlyObjectWrapper currentPath = new ReadOnlyObjectWrapper<>(); + private final NavigationHistory history = new NavigationHistory(); + + public Model() { + currentPath.set(USER_HOME); + history.append(USER_HOME); + } + + /////////////////////////////////////////////////////////////////////////// + // Properties // + /////////////////////////////////////////////////////////////////////////// + + public ReadOnlyObjectProperty currentPathProperty() { return currentPath.getReadOnlyProperty(); } + + public NavigationHistory getHistory() { return history; } + + /////////////////////////////////////////////////////////////////////////// + // Commands // + /////////////////////////////////////////////////////////////////////////// + + public void back() { + history.back().ifPresent(currentPath::set); + } + + public void forth() { + history.forth().ifPresent(currentPath::set); + } + + public void navigate(Path path, boolean saveInHistory) { + currentPath.set(Objects.requireNonNullElse(path, USER_HOME)); + if (saveInHistory) { history.append(path); } + } +} diff --git a/sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/NavigationHistory.java b/sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/NavigationHistory.java new file mode 100644 index 0000000..9fa09aa --- /dev/null +++ b/sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/NavigationHistory.java @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: MIT */ +package atlantafx.sampler.page.showcase.filemanager; + +import javafx.beans.binding.Bindings; +import javafx.beans.binding.BooleanBinding; +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.SimpleIntegerProperty; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +final class NavigationHistory { + + private final IntegerProperty cursor = new SimpleIntegerProperty(0); + private final List history = new ArrayList<>(); + private final BooleanBinding canGoBack = Bindings.createBooleanBinding( + () -> cursor.get() > 0 && history.size() > 1, cursor); + private final BooleanBinding canGoForth = Bindings.createBooleanBinding( + () -> cursor.get() < history.size() - 1, cursor); + + public void append(Path path) { + if (path == null) { return; } + var lastPath = history.size() > 0 ? history.get(history.size() - 1) : null; + if (!Objects.equals(lastPath, path)) { history.add(path); } + cursor.set(history.size() - 1); + } + + public Optional back() { + if (!canGoBack.get()) { return Optional.empty(); } + cursor.set(cursor.get() - 1); + return Optional.of(history.get(cursor.get())); + } + + public Optional forth() { + if (!canGoForth.get()) { return Optional.empty(); } + cursor.set(cursor.get() + 1); + return Optional.of(history.get(cursor.get())); + } + + public BooleanBinding canGoBackProperty() { + return canGoBack; + } + + public BooleanBinding canGoForthProperty() { + return canGoForth; + } +} diff --git a/sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/TableDirectoryView.java b/sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/TableDirectoryView.java new file mode 100644 index 0000000..568eaa4 --- /dev/null +++ b/sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/TableDirectoryView.java @@ -0,0 +1,185 @@ +/* SPDX-License-Identifier: MIT */ +package atlantafx.sampler.page.showcase.filemanager; + +import atlantafx.sampler.util.Containers; +import javafx.beans.property.SimpleLongProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.css.PseudoClass; +import javafx.geometry.Insets; +import javafx.scene.control.TableCell; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableRow; +import javafx.scene.control.TableView; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.Pane; +import org.kordamp.ikonli.javafx.FontIcon; +import org.kordamp.ikonli.material2.Material2AL; +import org.kordamp.ikonli.material2.Material2OutlinedAL; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.FileTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.Comparator; +import java.util.function.Consumer; +import java.util.stream.Stream; + +import static atlantafx.sampler.page.showcase.filemanager.Utils.*; +import static javafx.scene.control.TableColumn.SortType.ASCENDING; + +final class TableDirectoryView extends AnchorPane implements DirectoryView { + + private static final PseudoClass HIDDEN = PseudoClass.getPseudoClass("hidden"); + private static final PseudoClass FOLDER = PseudoClass.getPseudoClass("folder"); + + private final FileList fileList; + private Consumer actionHandler; + + public TableDirectoryView() { + TableView table = createTable(); + fileList = new FileList(table); + + getChildren().setAll(table); + getStyleClass().add("table-directory-view"); + Containers.setAnchors(table, Insets.EMPTY); + } + + @SuppressWarnings("unchecked") + private TableView createTable() { + var filenameCol = new TableColumn("Name"); + filenameCol.setCellValueFactory(param -> new SimpleStringProperty( + param.getValue() != null ? param.getValue().getFileName().toString() : null + )); + filenameCol.setComparator(Comparator.comparing(String::toLowerCase)); + filenameCol.setSortType(ASCENDING); + filenameCol.setCellFactory(col -> new FilenameCell()); + + var sizeCol = new TableColumn("Size"); + sizeCol.setCellValueFactory(param -> new SimpleLongProperty(fileSize(param.getValue()))); + sizeCol.setCellFactory(col -> new FileSizeCell()); + + var mtimeCol = new TableColumn("Modified"); + mtimeCol.setCellValueFactory(param -> new SimpleObjectProperty<>(fileMTime(param.getValue()))); + mtimeCol.setCellFactory(col -> new FileTimeCell()); + + // ~ + + var table = new TableView(); + table.getColumns().setAll(filenameCol, sizeCol, mtimeCol); + table.getSortOrder().add(filenameCol); + table.setSortPolicy(param -> true); + table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); + filenameCol.minWidthProperty().bind(table.widthProperty().multiply(0.5)); + table.setRowFactory(param -> { + TableRow row = new TableRow<>(); + + row.setOnMouseClicked(e -> { + if (e.getClickCount() == 2 && !row.isEmpty() && actionHandler != null) { + actionHandler.accept(row.getItem()); + } + }); + + return row; + }); + + return table; + } + + @Override + public Pane getView() { + return this; + } + + @Override + public void setDirectory(Path path) { + if (path == null) { + fileList.clear(); + } else { + try (Stream stream = Files.list(path)) { + fileList.load(stream); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + @Override + public void setOnAction(Consumer actionHandler) { + this.actionHandler = actionHandler; + } + + @Override + public FileList getFileList() { + return fileList; + } + + /////////////////////////////////////////////////////////////////////////// + + private static class FilenameCell extends TableCell { + + private final FontIcon icon = new FontIcon(); + + public FilenameCell() { + setGraphicTextGap(10); + } + + @Override + protected void updateItem(String filename, boolean empty) { + super.updateItem(filename, empty); + if (empty || getTableRow() == null || getTableRow().getItem() == null) { + setGraphic(null); + setText(null); + } else { + boolean isDirectory = Files.isDirectory(getTableRow().getItem()); + icon.setIconCode(isDirectory ? Material2AL.FOLDER : Material2OutlinedAL.INSERT_DRIVE_FILE); + pseudoClassStateChanged(FOLDER, isDirectory); + getTableRow().pseudoClassStateChanged(HIDDEN, isFileHidden(getTableRow().getItem())); + setGraphic(icon); + setText(filename); + } + } + } + + private static class FileSizeCell extends TableCell { + + @Override + protected void updateItem(Number fileSize, boolean empty) { + super.updateItem(fileSize, empty); + if (empty || getTableRow() == null || getTableRow().getItem() == null) { + setText(null); + } else { + Path path = getTableRow().getItem(); + if (Files.isDirectory(path)) { + if (Files.isReadable(path)) { + try (Stream stream = Files.list(path)) { + setText(stream.count() + " items"); + } catch (Exception e) { + throw new RuntimeException(e); + } + } else { + setText("unknown"); + } + } else { + setText(humanReadableByteCount(fileSize.longValue())); + } + } + } + } + + private static class FileTimeCell extends TableCell { + + @Override + protected void updateItem(FileTime fileTime, boolean empty) { + super.updateItem(fileTime, empty); + if (empty) { + setText(null); + } else { + setText(DateTimeFormatter.ISO_DATE.format( + fileTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime() + )); + } + } + } +} diff --git a/sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/Utils.java b/sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/Utils.java new file mode 100644 index 0000000..fa78b44 --- /dev/null +++ b/sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/Utils.java @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: MIT */ +package atlantafx.sampler.page.showcase.filemanager; + +import java.awt.*; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.FileTime; +import java.text.CharacterIterator; +import java.text.StringCharacterIterator; + +final class Utils { + + public static long fileSize(Path path) { + if (path == null) { return 0; } + try { + return Files.size(path); + } catch (IOException e) { + return 0; + } + } + + public static boolean isFileHidden(Path path) { + if (path == null) { return false; } + try { + return Files.isHidden(path); + } catch (IOException e) { + return false; + } + } + + public static FileTime fileMTime(Path path) { + if (path == null) { return null; } + try { + return Files.getLastModifiedTime(path); + } catch (IOException e) { + return null; + } + } + + public static String humanReadableByteCount(long bytes) { + if (-1000 < bytes && bytes < 1000) { return bytes + " B"; } + CharacterIterator ci = new StringCharacterIterator("kMGTPE"); + while (bytes <= -999_950 || bytes >= 999_950) { + bytes /= 1000; + ci.next(); + } + return String.format("%.1f %cB", bytes / 1000.0, ci.current()); + } + + public static void openFile(Path path) { + if (Desktop.isDesktopSupported()) { + new Thread(() -> { + try { + Desktop.getDesktop().open(path.toFile()); + } catch (IOException e) { + throw new RuntimeException(); + } + }).start(); + } + } +} diff --git a/sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/file-manager.css b/sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/file-manager.css new file mode 100644 index 0000000..5daaa18 --- /dev/null +++ b/sampler/src/main/java/atlantafx/sampler/page/showcase/filemanager/file-manager.css @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: MIT */ + +.file-manager-showcase .bookmark-list { + -fx-border-width: 0 0 1 1; +} +.file-manager-showcase .table-directory-view .table-view { + -fx-border-width: 0 1 1 0; +} + +/* setting opacity directly to the .table-cell or .table-row-cell + leads to incorrect table row height calculation #javafx-bug */ +.file-manager-showcase .table-row-cell:hidden >.table-cell > * { + -fx-opacity: 0.6; +} + +.file-manager-showcase .table-row-cell >.table-cell:folder .ikonli-font-icon { + -fx-icon-color: -color-accent-emphasis; + -fx-fill: -color-accent-emphasis; +} + +.file-manager-showcase .bookmark-list .ikonli-font-icon { + -fx-icon-size: 18px; +} + +.file-manager-showcase .table-directory-view .ikonli-font-icon { + -fx-icon-size: 18px; +} diff --git a/sampler/src/main/java/atlantafx/sampler/page/showcase/musicplayer/ColorThief.java b/sampler/src/main/java/atlantafx/sampler/page/showcase/musicplayer/ColorThief.java new file mode 100644 index 0000000..c10cbad --- /dev/null +++ b/sampler/src/main/java/atlantafx/sampler/page/showcase/musicplayer/ColorThief.java @@ -0,0 +1,583 @@ +/* + * Java Color Thief + * by Sven Woltmann, Fonpit AG + * https://androidpit.com + * https://androidpit.de + * + * Creative Commons Attribution 2.5 License: + * http://creativecommons.org/licenses/by/2.5/ + */ +package atlantafx.sampler.page.showcase.musicplayer; + +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferByte; +import java.util.*; + +import static java.awt.image.BufferedImage.TYPE_3BYTE_BGR; +import static java.awt.image.BufferedImage.TYPE_4BYTE_ABGR; + +final class ColorThief { + + private static final int DEFAULT_QUALITY = 10; + private static final boolean DEFAULT_IGNORE_WHITE = true; + + public static int[] getColor(BufferedImage source) { + int[][] palette = getPalette(source, 5); + if (palette == null) { return null; } + return palette[0]; + } + + public static int[][] getPalette(BufferedImage source, int colorCount) { + MMCQ.ColorMap colorMap = getColorMap(source, colorCount); + if (colorMap == null) { return null; } + return colorMap.palette(); + } + + public static MMCQ.ColorMap getColorMap(BufferedImage source, int colorCount) { + return getColorMap(source, colorCount, DEFAULT_QUALITY, DEFAULT_IGNORE_WHITE); + } + + public static MMCQ.ColorMap getColorMap(BufferedImage sourceImage, int colorCount, int quality, + boolean ignoreWhite) { + if (colorCount < 2 || colorCount > 256) { + throw new IllegalArgumentException("Specified colorCount must be between 2 and 256."); + } + if (quality < 1) { + throw new IllegalArgumentException("Specified quality should be greater then 0."); + } + + int[][] pixelArray = switch (sourceImage.getType()) { + case TYPE_3BYTE_BGR, TYPE_4BYTE_ABGR -> getPixelsFast(sourceImage, quality, ignoreWhite); + default -> getPixelsSlow(sourceImage, quality, ignoreWhite); + }; + + return MMCQ.quantize(pixelArray, colorCount); + } + + private static int[][] getPixelsFast( + BufferedImage sourceImage, + int quality, + boolean ignoreWhite) { + DataBufferByte imageData = (DataBufferByte) sourceImage.getRaster().getDataBuffer(); + byte[] pixels = imageData.getData(); + int pixelCount = sourceImage.getWidth() * sourceImage.getHeight(); + + int colorDepth; + int type = sourceImage.getType(); + colorDepth = switch (type) { + case TYPE_3BYTE_BGR -> 3; + case TYPE_4BYTE_ABGR -> 4; + default -> throw new IllegalArgumentException("Unhandled type: " + type); + }; + + int expectedDataLength = pixelCount * colorDepth; + if (expectedDataLength != pixels.length) { + throw new IllegalArgumentException( + "(expectedDataLength = " + expectedDataLength + ") != (pixels.length = " + pixels.length + ")" + ); + } + + int numRegardedPixels = (pixelCount + quality - 1) / quality; + + int numUsedPixels = 0; + int[][] pixelArray = new int[numRegardedPixels][]; + int offset, r, g, b, a; + + switch (type) { + case TYPE_3BYTE_BGR: + for (int i = 0; i < pixelCount; i += quality) { + offset = i * 3; + b = pixels[offset] & 0xFF; + g = pixels[offset + 1] & 0xFF; + r = pixels[offset + 2] & 0xFF; + + if (!(ignoreWhite && r > 250 && g > 250 && b > 250)) { + pixelArray[numUsedPixels] = new int[] { r, g, b }; + numUsedPixels++; + } + } + break; + + case TYPE_4BYTE_ABGR: + for (int i = 0; i < pixelCount; i += quality) { + offset = i * 4; + a = pixels[offset] & 0xFF; + b = pixels[offset + 1] & 0xFF; + g = pixels[offset + 2] & 0xFF; + r = pixels[offset + 3] & 0xFF; + + if (a >= 125 && !(ignoreWhite && r > 250 && g > 250 && b > 250)) { + pixelArray[numUsedPixels] = new int[] { r, g, b }; + numUsedPixels++; + } + } + break; + + default: + throw new IllegalArgumentException("Unhandled type: " + type); + } + + return Arrays.copyOfRange(pixelArray, 0, numUsedPixels); + } + + private static int[][] getPixelsSlow( + BufferedImage sourceImage, + int quality, + boolean ignoreWhite) { + int width = sourceImage.getWidth(); + int height = sourceImage.getHeight(); + + int pixelCount = width * height; + int numRegardedPixels = (pixelCount + quality - 1) / quality; + int numUsedPixels = 0; + + int[][] res = new int[numRegardedPixels][]; + int r, g, b; + + for (int i = 0; i < pixelCount; i += quality) { + int row = i / width; + int col = i % width; + int rgb = sourceImage.getRGB(col, row); + + r = (rgb >> 16) & 0xFF; + g = (rgb >> 8) & 0xFF; + b = (rgb) & 0xFF; + if (!(ignoreWhite && r > 250 && g > 250 && b > 250)) { + res[numUsedPixels] = new int[] { r, g, b }; + numUsedPixels++; + } + } + + return Arrays.copyOfRange(res, 0, numUsedPixels); + } + + private static class MMCQ { + + private static final int SIGBITS = 5; + private static final int RSHIFT = 8 - SIGBITS; + private static final int MULT = 1 << RSHIFT; + private static final int HISTOSIZE = 1 << (3 * SIGBITS); + private static final int VBOX_LENGTH = 1 << SIGBITS; + private static final double FRACT_BY_POPULATION = 0.75; + private static final int MAX_ITERATIONS = 1000; + + static int getColorIndex(int r, int g, int b) { + return (r << (2 * SIGBITS)) + (g << SIGBITS) + b; + } + + public static class VBox { + int r1; + int r2; + int g1; + int g2; + int b1; + int b2; + + private final int[] histo; + + private int[] _avg; + private Integer _volume; + private Integer _count; + + public VBox(int r1, int r2, int g1, int g2, int b1, int b2, int[] histo) { + this.r1 = r1; + this.r2 = r2; + this.g1 = g1; + this.g2 = g2; + this.b1 = b1; + this.b2 = b2; + this.histo = histo; + } + + @Override + public String toString() { + return "r1: " + r1 + " / r2: " + r2 + " / g1: " + g1 + " / g2: " + g2 + " / b1: " + b1 + " / b2: " + b2; + } + + public int volume(boolean force) { + if (_volume == null || force) { + _volume = ((r2 - r1 + 1) * (g2 - g1 + 1) * (b2 - b1 + 1)); + } + return _volume; + } + + public int count(boolean force) { + if (_count == null || force) { + int npix = 0; + int i, j, k, index; + + for (i = r1; i <= r2; i++) { + for (j = g1; j <= g2; j++) { + for (k = b1; k <= b2; k++) { + index = getColorIndex(i, j, k); + npix += histo[index]; + } + } + } + + _count = npix; + } + + return _count; + } + + @Override + @SuppressWarnings("MethodDoesntCallSuperMethod") + public VBox clone() { + return new VBox(r1, r2, g1, g2, b1, b2, histo); + } + + public int[] avg(boolean force) { + if (_avg == null || force) { + int ntot = 0; + int rsum = 0; + int gsum = 0; + int bsum = 0; + int hval, i, j, k, histoindex; + + for (i = r1; i <= r2; i++) { + for (j = g1; j <= g2; j++) { + for (k = b1; k <= b2; k++) { + histoindex = getColorIndex(i, j, k); + hval = histo[histoindex]; + ntot += hval; + rsum += (hval * (i + 0.5) * MULT); + gsum += (hval * (j + 0.5) * MULT); + bsum += (hval * (k + 0.5) * MULT); + } + } + } + + if (ntot > 0) { + _avg = new int[] { (rsum / ntot), (gsum / ntot), (bsum / ntot) }; + } else { + _avg = new int[] { (MULT * (r1 + r2 + 1) / 2), (MULT * (g1 + g2 + 1) / 2), (MULT * (b1 + b2 + 1) / 2) }; + } + } + + return _avg; + } + + public boolean contains(int[] pixel) { + int rval = pixel[0] >> RSHIFT; + int gval = pixel[1] >> RSHIFT; + int bval = pixel[2] >> RSHIFT; + return (rval >= r1 && rval <= r2 && gval >= g1 && gval <= g2 && bval >= b1 && bval <= b2); + } + } + + public static class ColorMap { + + public final ArrayList vboxes = new ArrayList<>(); + + public void push(VBox box) { + vboxes.add(box); + } + + public int[][] palette() { + int numVBoxes = vboxes.size(); + int[][] palette = new int[numVBoxes][]; + for (int i = 0; i < numVBoxes; i++) { + palette[i] = vboxes.get(i).avg(false); + } + return palette; + } + + public int size() { + return vboxes.size(); + } + + public int[] map(int[] color) { + int numVBoxes = vboxes.size(); + for (VBox vbox : vboxes) { + if (vbox.contains(color)) { + return vbox.avg(false); + } + } + return nearest(color); + } + + public int[] nearest(int[] color) { + double d1 = Double.MAX_VALUE; + double d2; + int[] pColor = null; + + int numVBoxes = vboxes.size(); + for (VBox vbox : vboxes) { + int[] vbColor = vbox.avg(false); + d2 = Math.sqrt(Math.pow(color[0] - vbColor[0], 2) + + Math.pow(color[1] - vbColor[1], 2) + + Math.pow(color[2] - vbColor[2], 2) + ); + if (d2 < d1) { + d1 = d2; + pColor = vbColor; + } + } + return pColor; + } + + } + + private static int[] getHisto(int[][] pixels) { + int[] histo = new int[HISTOSIZE]; + int index, rval, gval, bval; + int numPixels = pixels.length; + + for (int[] pixel : pixels) { + rval = pixel[0] >> RSHIFT; + gval = pixel[1] >> RSHIFT; + bval = pixel[2] >> RSHIFT; + index = getColorIndex(rval, gval, bval); + histo[index]++; + } + return histo; + } + + private static VBox vboxFromPixels(int[][] pixels, int[] histo) { + int rmin = 1000000, rmax = 0; + int gmin = 1000000, gmax = 0; + int bmin = 1000000, bmax = 0; + + int rval, gval, bval; + + int numPixels = pixels.length; + for (int[] pixel : pixels) { + rval = pixel[0] >> RSHIFT; + gval = pixel[1] >> RSHIFT; + bval = pixel[2] >> RSHIFT; + + if (rval < rmin) { + rmin = rval; + } else if (rval > rmax) { + rmax = rval; + } + + if (gval < gmin) { + gmin = gval; + } else if (gval > gmax) { + gmax = gval; + } + + if (bval < bmin) { + bmin = bval; + } else if (bval > bmax) { + bmax = bval; + } + } + + return new VBox(rmin, rmax, gmin, gmax, bmin, bmax, histo); + } + + private static VBox[] medianCutApply(int[] histo, VBox vbox) { + if (vbox.count(false) == 0) { + return null; + } + + if (vbox.count(false) == 1) { + return new VBox[] { vbox.clone(), null }; + } + + int rw = vbox.r2 - vbox.r1 + 1; + int gw = vbox.g2 - vbox.g1 + 1; + int bw = vbox.b2 - vbox.b1 + 1; + int maxw = Math.max(Math.max(rw, gw), bw); + + int total = 0; + int[] partialSum = new int[VBOX_LENGTH]; + Arrays.fill(partialSum, -1); + int[] lookAheadSum = new int[VBOX_LENGTH]; + Arrays.fill(lookAheadSum, -1); + int i, j, k, sum, index; + + if (maxw == rw) { + for (i = vbox.r1; i <= vbox.r2; i++) { + sum = 0; + for (j = vbox.g1; j <= vbox.g2; j++) { + for (k = vbox.b1; k <= vbox.b2; k++) { + index = getColorIndex(i, j, k); + sum += histo[index]; + } + } + total += sum; + partialSum[i] = total; + } + } else if (maxw == gw) { + for (i = vbox.g1; i <= vbox.g2; i++) { + sum = 0; + for (j = vbox.r1; j <= vbox.r2; j++) { + for (k = vbox.b1; k <= vbox.b2; k++) { + index = getColorIndex(j, i, k); + sum += histo[index]; + } + } + total += sum; + partialSum[i] = total; + } + } else { + for (i = vbox.b1; i <= vbox.b2; i++) { + sum = 0; + for (j = vbox.r1; j <= vbox.r2; j++) { + for (k = vbox.g1; k <= vbox.g2; k++) { + index = getColorIndex(j, k, i); + sum += histo[index]; + } + } + total += sum; + partialSum[i] = total; + } + } + + for (i = 0; i < VBOX_LENGTH; i++) { + if (partialSum[i] != -1) { + lookAheadSum[i] = total - partialSum[i]; + } + } + + return maxw == rw ? doCut('r', vbox, partialSum, lookAheadSum, total) + : maxw == gw ? doCut('g', vbox, partialSum, lookAheadSum, total) + : doCut('b', vbox, partialSum, lookAheadSum, total); + } + + private static VBox[] doCut( + char color, + VBox vbox, + int[] partialSum, + int[] lookAheadSum, + int total + ) { + int vbox_dim1; + int vbox_dim2; + + if (color == 'r') { + vbox_dim1 = vbox.r1; + vbox_dim2 = vbox.r2; + } else if (color == 'g') { + vbox_dim1 = vbox.g1; + vbox_dim2 = vbox.g2; + } else { + vbox_dim1 = vbox.b1; + vbox_dim2 = vbox.b2; + } + + int left, right; + VBox vbox1, vbox2; + int d2, count2; + + for (int i = vbox_dim1; i <= vbox_dim2; i++) { + if (partialSum[i] > total / 2) { + vbox1 = vbox.clone(); + vbox2 = vbox.clone(); + + left = i - vbox_dim1; + right = vbox_dim2 - i; + + if (left <= right) { + d2 = Math.min(vbox_dim2 - 1, (i + right / 2)); + } else { + d2 = Math.max(vbox_dim1, ((int) (i - 1 - left / 2.0))); + } + + while (d2 < 0 || partialSum[d2] <= 0) { + d2++; + } + count2 = lookAheadSum[d2]; + while (count2 == 0 && d2 > 0 && partialSum[d2 - 1] > 0) { + count2 = lookAheadSum[--d2]; + } + + if (color == 'r') { + vbox1.r2 = d2; + vbox2.r1 = d2 + 1; + } else if (color == 'g') { + vbox1.g2 = d2; + vbox2.g1 = d2 + 1; + } else { + vbox1.b2 = d2; + vbox2.b1 = d2 + 1; + } + + return new VBox[] { vbox1, vbox2 }; + } + } + + throw new RuntimeException("VBox can't be cut"); + } + + public static ColorMap quantize(int[][] pixels, int maxcolors) { + if (pixels.length == 0 || maxcolors < 2 || maxcolors > 256) { return null; } + + int[] histo = getHisto(pixels); + + VBox vbox = vboxFromPixels(pixels, histo); + ArrayList pq = new ArrayList<>(); + pq.add(vbox); + + int target = (int) Math.ceil(FRACT_BY_POPULATION * maxcolors); + + iter(pq, COMPARATOR_COUNT, target, histo); + pq.sort(COMPARATOR_PRODUCT); + + if (maxcolors > pq.size()) { + iter(pq, COMPARATOR_PRODUCT, maxcolors, histo); + } + + Collections.reverse(pq); + + ColorMap colorMap = new ColorMap(); + for (VBox vb : pq) { + colorMap.push(vb); + } + + return colorMap; + } + + @SuppressWarnings("ConstantConditions") + private static void iter(List lh, Comparator comparator, int target, int[] histo) { + int niters = 0; + VBox vbox; + + while (niters < MAX_ITERATIONS) { + vbox = lh.get(lh.size() - 1); + if (vbox.count(false) == 0) { + lh.sort(comparator); + niters++; + continue; + } + lh.remove(lh.size() - 1); + + VBox[] vboxes = medianCutApply(histo, vbox); + VBox vbox1 = vboxes[0]; + VBox vbox2 = vboxes[1]; + + if (vbox1 == null) { + throw new RuntimeException("vbox1 not defined; shouldn't happen!"); + } + + lh.add(vbox1); + if (vbox2 != null) { + lh.add(vbox2); + } + lh.sort(comparator); + + if (lh.size() >= target) { + return; + } + if (niters++ > MAX_ITERATIONS) { return; } + } + } + + private static final Comparator COMPARATOR_COUNT = Comparator.comparingInt(a -> a.count(false)); + + private static final Comparator COMPARATOR_PRODUCT = (a, b) -> { + int aCount = a.count(false); + int bCount = b.count(false); + int aVolume = a.volume(false); + int bVolume = b.volume(false); + + if (aCount == bCount) { return aVolume - bVolume; } + + return Long.compare((long) aCount * aVolume, (long) bCount * bVolume); + }; + } +} diff --git a/sampler/src/main/java/atlantafx/sampler/page/showcase/musicplayer/MediaFile.java b/sampler/src/main/java/atlantafx/sampler/page/showcase/musicplayer/MediaFile.java new file mode 100644 index 0000000..3ee4201 --- /dev/null +++ b/sampler/src/main/java/atlantafx/sampler/page/showcase/musicplayer/MediaFile.java @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: MIT */ +package atlantafx.sampler.page.showcase.musicplayer; + +import atlantafx.sampler.Resources; +import javafx.scene.image.Image; +import javafx.scene.media.Media; +import javafx.scene.media.MediaPlayer; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; + +import static atlantafx.sampler.page.showcase.musicplayer.MediaFile.Metadata.*; +import static atlantafx.sampler.page.showcase.musicplayer.Utils.copyImage; + +@SuppressWarnings("StringOperationCanBeSimplified") +record MediaFile(File file) { + + private static final Map METADATA_CACHE = new HashMap<>(); + + // Sadly JavaFX Media API is not user-friendly. If you want to obtain any + // media file metadata you have to load it to media player instance, which + // is costly and that instance is not even reusable. + public void readMetadata(Consumer callback) { + var media = new Media(file.toURI().toString()); + var mediaPlayer = new MediaPlayer(media); + + // The media information is obtained asynchronously and so not necessarily + // available immediately after instantiation of the class. All information + // should however be available if the instance has been associated with a + // MediaPlayer and that player has transitioned to Status.READY status. + mediaPlayer.setOnReady(() -> { + Map metadata = media.getMetadata(); + callback.accept(METADATA_CACHE.computeIfAbsent(file.getAbsolutePath(), k -> + // clone everything to make sure media player will be garbage collected + new Metadata( + new String(getTag(metadata, "title", String.class, NO_TITLE)), + copyImage(getTag(metadata, "image", Image.class, NO_IMAGE)), + new String(getTag(metadata, "artist", String.class, NO_ARTIST)), + new String(getTag(metadata, "album", String.class, NO_ALBUM)), + media.getDuration().toMillis() + )) + ); + + mediaPlayer.dispose(); + }); + } + + public Media createMedia() { + return new Media(file.toURI().toString()); + } + + private T getTag(Map metadata, String key, Class type, T defaultValue) { + Object tag = metadata.get(key); + return type.isInstance(tag) ? type.cast(tag) : defaultValue; + } + + /////////////////////////////////////////////////////////////////////////// + + record Metadata(String title, Image image, String artist, String album, double duration) { + + static final Image NO_IMAGE = new Image( + Resources.getResourceAsStream("images/no-image.png"), 150, 150, true, false + ); + static final String NO_TITLE = "Unknown title"; + static final String NO_ARTIST = "Unknown artist"; + static final String NO_ALBUM = "Unknown album"; + } +} \ No newline at end of file diff --git a/sampler/src/main/java/atlantafx/sampler/page/showcase/musicplayer/Model.java b/sampler/src/main/java/atlantafx/sampler/page/showcase/musicplayer/Model.java new file mode 100644 index 0000000..353788d --- /dev/null +++ b/sampler/src/main/java/atlantafx/sampler/page/showcase/musicplayer/Model.java @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: MIT */ +package atlantafx.sampler.page.showcase.musicplayer; + +import javafx.beans.binding.Bindings; +import javafx.beans.property.*; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.scene.paint.Color; + +import java.util.Objects; + +class Model { + + private final ObservableList playlist = FXCollections.observableArrayList(); + private final IntegerProperty playlistIndex = new SimpleIntegerProperty(); + private final ReadOnlyBooleanWrapper canGoBack = new ReadOnlyBooleanWrapper(); + private final ReadOnlyBooleanWrapper canGoForward = new ReadOnlyBooleanWrapper(); + private final ReadOnlyObjectWrapper currentTrack = new ReadOnlyObjectWrapper<>(); + private final ReadOnlyObjectWrapper backgroundColor = new ReadOnlyObjectWrapper<>(Color.TRANSPARENT); + + public Model() { + canGoBack.bind(Bindings.createBooleanBinding( + () -> playlist.size() > 1 && getPlaylistPosition() > 0, currentTrack) + ); + canGoForward.bind(Bindings.createBooleanBinding( + () -> playlist.size() > 0 && getPlaylistPosition() < playlist.size() - 1, currentTrack)); + } + + private int getPlaylistPosition() { + if (currentTrack.get() == null) { return -1; } + return playlist.indexOf(currentTrack.get()); + } + + /////////////////////////////////////////////////////////////////////////// + // Properties // + /////////////////////////////////////////////////////////////////////////// + + public ObservableList playlist() { return playlist; } + + public ReadOnlyBooleanProperty canGoBackProperty() { return canGoBack.getReadOnlyProperty(); } + + public ReadOnlyBooleanProperty canGoForwardProperty() { return canGoForward.getReadOnlyProperty(); } + + public ReadOnlyObjectProperty currentTrackProperty() { return currentTrack.getReadOnlyProperty(); } + + public ReadOnlyObjectProperty backgroundColorProperty() { return backgroundColor.getReadOnlyProperty(); } + + /////////////////////////////////////////////////////////////////////////// + // Commands // + /////////////////////////////////////////////////////////////////////////// + + public void play(MediaFile mediaFile) { + currentTrack.set(Objects.requireNonNull(mediaFile)); + } + + public void playPrevious() { + if (canGoBack.get()) { currentTrack.set(playlist.get(getPlaylistPosition() - 1)); } + } + + public void playNext() { + if (canGoForward.get()) { currentTrack.set(playlist.get(getPlaylistPosition() + 1)); } + } + + public void reset() { + currentTrack.set(null); + setBackgroundColor(null); + } + + public void setBackgroundColor(Color color) { + backgroundColor.set(Objects.requireNonNullElse(color, Color.TRANSPARENT)); + } + + public void shuffle() { + FXCollections.shuffle(playlist); + } + + public void addFile(MediaFile mediaFile) { + playlist.add(Objects.requireNonNull(mediaFile)); + } + + public void removeFile(MediaFile mediaFile) { + playlist.remove(Objects.requireNonNull(mediaFile)); + } + + public void removeAll() { + reset(); + playlist().clear(); + } +} diff --git a/sampler/src/main/java/atlantafx/sampler/page/showcase/musicplayer/MusicPlayerPage.java b/sampler/src/main/java/atlantafx/sampler/page/showcase/musicplayer/MusicPlayerPage.java new file mode 100644 index 0000000..edfb60c --- /dev/null +++ b/sampler/src/main/java/atlantafx/sampler/page/showcase/musicplayer/MusicPlayerPage.java @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: MIT */ +package atlantafx.sampler.page.showcase.musicplayer; + +import atlantafx.sampler.Resources; +import atlantafx.sampler.page.showcase.ShowcasePage; +import atlantafx.sampler.util.Containers; +import javafx.collections.ListChangeListener; +import javafx.geometry.Insets; +import javafx.scene.control.SplitPane; +import javafx.scene.image.Image; +import javafx.scene.layout.*; + +import java.util.Objects; + +public class MusicPlayerPage extends ShowcasePage { + + public static final String NAME = "Music Player"; + public static final double BACKGROUND_OPACITY = 0.1; + private static final String STYLESHEET_URL = Objects.requireNonNull( + MusicPlayerPage.class.getResource("music-player.css")).toExternalForm(); + private static final Image PLUG_IMAGE = new Image(Resources.getResourceAsStream("images/vinyl.jpg")); + + @Override + public String getName() { return NAME; } + + private final Model model = new Model(); + + public MusicPlayerPage() { + super(); + createView(); + } + + private void createView() { + var player = new Player(model); + player.setVisible(false); + + BackgroundImage backgroundImage = new BackgroundImage( + PLUG_IMAGE, + BackgroundRepeat.REPEAT, + BackgroundRepeat.REPEAT, + BackgroundPosition.DEFAULT, + BackgroundSize.DEFAULT + ); + var plug = new AnchorPane(); + plug.setBackground(new Background(backgroundImage)); + plug.setOpacity(0.5); + plug.setMouseTransparent(false); + + var playerStack = new StackPane(player, plug); + model.playlist().addListener((ListChangeListener) c -> { + if (model.playlist().size() > 0) { + player.setVisible(true); + plug.setVisible(false); + player.toFront(); + } else { + player.setVisible(false); + plug.setVisible(true); + plug.toFront(); + } + }); + + var playlist = new Playlist(model); + + var root = new SplitPane(); + root.getStylesheets().add(STYLESHEET_URL); + root.getStyleClass().add("music-player-showcase"); + root.getItems().setAll(playerStack, playlist); + + showcase.getChildren().setAll(root); + Containers.setAnchors(root, Insets.EMPTY); + } + + @Override + public void reset() { + super.reset(); + model.reset(); + } +} diff --git a/sampler/src/main/java/atlantafx/sampler/page/showcase/musicplayer/Player.java b/sampler/src/main/java/atlantafx/sampler/page/showcase/musicplayer/Player.java new file mode 100644 index 0000000..a1579c1 --- /dev/null +++ b/sampler/src/main/java/atlantafx/sampler/page/showcase/musicplayer/Player.java @@ -0,0 +1,262 @@ +/* SPDX-License-Identifier: MIT */ +package atlantafx.sampler.page.showcase.musicplayer; + +import atlantafx.base.controls.Popover; +import atlantafx.base.controls.Spacer; +import javafx.beans.InvalidationListener; +import javafx.beans.binding.Bindings; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.geometry.Insets; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.Slider; +import javafx.scene.control.Tooltip; +import javafx.scene.image.Image; +import javafx.scene.layout.*; +import javafx.scene.media.Media; +import javafx.scene.media.MediaPlayer; +import javafx.scene.paint.Color; +import javafx.scene.paint.ImagePattern; +import javafx.scene.shape.Circle; +import javafx.scene.shape.Rectangle; +import javafx.util.Duration; +import org.kordamp.ikonli.javafx.FontIcon; + +import static atlantafx.base.controls.Popover.ArrowLocation; +import static atlantafx.base.theme.Styles.*; +import static atlantafx.sampler.page.showcase.musicplayer.MediaFile.Metadata.*; +import static atlantafx.sampler.page.showcase.musicplayer.MusicPlayerPage.BACKGROUND_OPACITY; +import static atlantafx.sampler.page.showcase.musicplayer.Utils.formatDuration; +import static atlantafx.sampler.page.showcase.musicplayer.Utils.getDominantColor; +import static javafx.geometry.Orientation.VERTICAL; +import static javafx.geometry.Pos.CENTER; +import static org.kordamp.ikonli.material2.Material2AL.CLEAR_ALL; +import static org.kordamp.ikonli.material2.Material2AL.EQUALS; +import static org.kordamp.ikonli.material2.Material2MZ.*; +import static org.kordamp.ikonli.material2.Material2OutlinedAL.FAST_FORWARD; +import static org.kordamp.ikonli.material2.Material2OutlinedAL.FAST_REWIND; + +final class Player extends VBox { + + private static final int PANEL_MAX_WIDTH = 220; + + private final ObjectProperty currentPlayer = new SimpleObjectProperty<>(); + + public Player(Model model) { + Rectangle coverImage = new Rectangle(0, 0, 150, 150); + coverImage.setArcWidth(20.0); + coverImage.setArcHeight(20.0); + coverImage.setFill(new ImagePattern(NO_IMAGE)); + + var trackTitle = new Label(NO_TITLE); + trackTitle.setAlignment(CENTER); + trackTitle.setMaxWidth(Double.MAX_VALUE); + trackTitle.getStyleClass().add(TITLE_3); + + var trackArtist = new Label(NO_ARTIST); + trackArtist.setAlignment(CENTER); + trackArtist.setMaxWidth(Double.MAX_VALUE); + + var trackAlbum = new Label(NO_ALBUM); + trackAlbum.setAlignment(CENTER); + trackAlbum.setMaxWidth(Double.MAX_VALUE); + + // == Media controls == + + var prevBtn = new Button("", new FontIcon(FAST_REWIND)); + prevBtn.getStyleClass().addAll(BUTTON_CIRCLE); + prevBtn.setShape(new Circle(50)); + prevBtn.setTooltip(new Tooltip("Previous")); + prevBtn.disableProperty().bind(model.canGoBackProperty().not()); + prevBtn.setOnAction(e -> model.playPrevious()); + + var playIcon = new FontIcon(PLAY_ARROW); + playIcon.setIconSize(32); + + var playBtn = new Button("", playIcon); + playBtn.getStyleClass().addAll(BUTTON_CIRCLE); + playBtn.setShape(new Circle(50)); + + var nextBtn = new Button("", new FontIcon(FAST_FORWARD)); + nextBtn.getStyleClass().addAll(BUTTON_CIRCLE); + nextBtn.setShape(new Circle(50)); + nextBtn.disableProperty().bind(model.canGoForwardProperty().not()); + nextBtn.setOnAction(e -> model.playNext()); + nextBtn.setTooltip(new Tooltip("Next")); + + var mediaControls = new HBox(20); + mediaControls.getChildren().setAll(prevBtn, playBtn, nextBtn); + mediaControls.setAlignment(CENTER); + + // == Time controls == + + var timeSlider = new Slider(0, 1, 0); + timeSlider.setMaxWidth(Double.MAX_VALUE); + timeSlider.setMinWidth(PANEL_MAX_WIDTH); + timeSlider.setMaxWidth(PANEL_MAX_WIDTH); + + var currentTimeLabel = new Label("0.0"); + currentTimeLabel.getStyleClass().add(TEXT_SMALL); + + var endTimeLabel = new Label("5.0"); + endTimeLabel.getStyleClass().add(TEXT_SMALL); + + var timeMarkersBox = new HBox(5); + timeMarkersBox.getChildren().setAll(currentTimeLabel, new Spacer(), endTimeLabel); + timeMarkersBox.setMaxWidth(PANEL_MAX_WIDTH); + + // == Extra controls == + + var clearPlaylistBtn = new Button("", new FontIcon(CLEAR_ALL)); + clearPlaylistBtn.getStyleClass().addAll(BUTTON_CIRCLE); + clearPlaylistBtn.setShape(new Circle(50)); + clearPlaylistBtn.setTooltip(new Tooltip("Clear")); + clearPlaylistBtn.setOnAction(e -> model.removeAll()); + + var shuffleBtn = new Button("", new FontIcon(SHUFFLE)); + shuffleBtn.getStyleClass().addAll(BUTTON_CIRCLE); + shuffleBtn.setShape(new Circle(50)); + shuffleBtn.setTooltip(new Tooltip("Shuffle")); + shuffleBtn.setOnAction(e -> model.shuffle()); + + var volumeSlider = new Slider(0, 1, 0.75); + volumeSlider.setOrientation(VERTICAL); + + var volumeBar = new VBox(5); + volumeBar.getChildren().setAll(new FontIcon(VOLUME_UP), volumeSlider, new FontIcon(VOLUME_OFF)); + volumeBar.setAlignment(CENTER); + + var volumePopover = new Popover(volumeBar); + volumePopover.setHeaderAlwaysVisible(false); + volumePopover.setArrowLocation(ArrowLocation.TOP_LEFT); + + var volumeBtn = new Button("", new FontIcon(VOLUME_UP)); + volumeBtn.getStyleClass().addAll(BUTTON_CIRCLE); + volumeBtn.setShape(new Circle(50)); + volumeBtn.setOnAction(e -> volumePopover.show(volumeBtn)); + + var extraControls = new HBox(10); + extraControls.getChildren().setAll(clearPlaylistBtn, shuffleBtn, new Spacer(), volumeBtn); + extraControls.setMaxWidth(PANEL_MAX_WIDTH); + + // == Root == + + setSpacing(5); + getStyleClass().add("player"); + setAlignment(CENTER); + getChildren().setAll( + new Spacer(VERTICAL), + new StackPane(coverImage), + new Spacer(10, VERTICAL), + trackTitle, + trackArtist, + trackAlbum, + new Spacer(20, VERTICAL), + mediaControls, + new Spacer(10, VERTICAL), + timeSlider, + timeMarkersBox, + new Spacer(10, VERTICAL), + extraControls, + new Spacer(VERTICAL) + ); + + // == Play == + + backgroundProperty().bind(Bindings.createObjectBinding(() -> { + Color color = model.backgroundColorProperty().get(); + return new Background(new BackgroundFill(color, CornerRadii.EMPTY, Insets.EMPTY)); + }, model.backgroundColorProperty())); + + playBtn.setOnAction(e -> { + MediaPlayer player = currentPlayer.get(); + if (player == null) { return; } + switch (player.getStatus()) { + case READY, PAUSED, STOPPED -> player.play(); + case PLAYING -> player.pause(); + default -> { } + } + }); + + InvalidationListener timeChangeListener = obs -> { + if (currentPlayer.get() == null) { return; } + + var duration = currentPlayer.get().getCurrentTime(); + var seconds = duration != null && !duration.equals(Duration.ZERO) ? duration.toSeconds() : 0; + + if (!timeSlider.isValueChanging()) { timeSlider.setValue(seconds); } + currentTimeLabel.setText(seconds > 0 ? formatDuration(duration) : "0.0"); + }; + + InvalidationListener sliderChangeListener = obs -> { + if (currentPlayer.get() == null) { return; } + long max = (long) currentPlayer.get().getMedia().getDuration().toSeconds(); + long sliderVal = (long) timeSlider.getValue(); + if (sliderVal <= max && timeSlider.isValueChanging()) { + currentPlayer.get().seek(Duration.seconds(sliderVal)); + } + }; + timeSlider.valueProperty().addListener(sliderChangeListener); + + model.currentTrackProperty().addListener((obs, old, val) -> { + if (val == null) { + coverImage.setFill(new ImagePattern(NO_IMAGE)); + trackTitle.setText(NO_TITLE); + trackArtist.setText(NO_ARTIST); + trackAlbum.setText(NO_ALBUM); + timeSlider.setValue(0); + currentPlayer.set(null); + return; + } + + Media media = val.createMedia(); + MediaPlayer mediaPlayer = new MediaPlayer(media); + mediaPlayer.setOnReady(() -> { + Image image = getTag(media, "image", Image.class, NO_IMAGE); + coverImage.setFill(new ImagePattern(image)); + model.setBackgroundColor(image != NO_IMAGE ? getDominantColor(image, BACKGROUND_OPACITY) : null); + + trackTitle.setText(getTag(media, "title", String.class, NO_TITLE)); + trackArtist.setText(getTag(media, "artist", String.class, NO_ARTIST)); + trackAlbum.setText(getTag(media, "album", String.class, NO_ALBUM)); + + timeSlider.setMax(media.getDuration().toSeconds()); + endTimeLabel.setText(formatDuration(media.getDuration())); + + playIcon.iconCodeProperty().bind(Bindings.createObjectBinding(() -> { + if (mediaPlayer.statusProperty().get() == null) { return EQUALS; } + return switch (mediaPlayer.getStatus()) { + case READY, PAUSED, STOPPED -> PLAY_ARROW; + case PLAYING -> PAUSE; + default -> EQUALS; + }; + }, mediaPlayer.statusProperty())); + + mediaPlayer.volumeProperty().bind(volumeSlider.valueProperty()); + mediaPlayer.currentTimeProperty().addListener(timeChangeListener); + }); + mediaPlayer.setOnEndOfMedia(model::playNext); + + currentPlayer.set(mediaPlayer); + mediaPlayer.play(); + }); + + // remove all listeners and dispose old player + currentPlayer.addListener((obs, old, val) -> { + if (old != null) { + old.stop(); + old.volumeProperty().unbind(); + old.currentTimeProperty().removeListener(timeChangeListener); + playIcon.iconCodeProperty().unbind(); + old.dispose(); + } + }); + } + + private T getTag(Media media, String key, Class type, T defaultValue) { + if (media == null || key == null || type == null) { return defaultValue; } + Object tag = media.getMetadata().get(key); + return type.isInstance(tag) ? type.cast(tag) : defaultValue; + } +} \ No newline at end of file diff --git a/sampler/src/main/java/atlantafx/sampler/page/showcase/musicplayer/Playlist.java b/sampler/src/main/java/atlantafx/sampler/page/showcase/musicplayer/Playlist.java new file mode 100644 index 0000000..e51df4d --- /dev/null +++ b/sampler/src/main/java/atlantafx/sampler/page/showcase/musicplayer/Playlist.java @@ -0,0 +1,179 @@ +/* SPDX-License-Identifier: MIT */ +package atlantafx.sampler.page.showcase.musicplayer; + +import atlantafx.base.controls.Spacer; +import javafx.application.Platform; +import javafx.collections.ListChangeListener; +import javafx.concurrent.Task; +import javafx.geometry.Insets; +import javafx.scene.control.*; +import javafx.scene.layout.*; +import javafx.scene.paint.ImagePattern; +import javafx.scene.shape.Rectangle; +import javafx.stage.FileChooser; +import org.kordamp.ikonli.javafx.FontIcon; + +import java.io.File; +import java.util.List; +import java.util.Objects; + +import static atlantafx.base.theme.Styles.*; +import static atlantafx.sampler.page.showcase.musicplayer.Utils.toWebColor; +import static java.lang.Double.MAX_VALUE; +import static javafx.geometry.Pos.CENTER_LEFT; +import static javafx.scene.layout.Priority.ALWAYS; +import static javafx.stage.FileChooser.ExtensionFilter; +import static org.kordamp.ikonli.material2.Material2AL.ADD; +import static org.kordamp.ikonli.material2.Material2MZ.PLAYLIST_PLAY; + +final class Playlist extends VBox { + + public Playlist(Model model) { + var headerLabel = new Label("Playlist"); + headerLabel.getStyleClass().setAll(TEXT_CAPTION); + // There's probably some #javafx-bug here. This label uses CSS class that + // changes font size & weight. When switching between themes it _sometimes_ + // ignores new color variables and remains using old fg color. Like it + // caches old font or something. The below rule forces it to use proper color. + headerLabel.setStyle("-fx-text-fill: -color-fg-default;"); + + var sizeLabel = new Label(""); + sizeLabel.getStyleClass().add(TEXT_SMALL); + + var sizeDescLabel = new Label("empty"); + sizeDescLabel.getStyleClass().add(TEXT_SMALL); + + var loadProgress = new ProgressBar(1); + loadProgress.getStyleClass().add(SMALL); + loadProgress.setMaxWidth(MAX_VALUE); + loadProgress.setVisible(false); + + var addButton = new Button("Add", new FontIcon(ADD)); + + var controlsBox = new HBox(); + controlsBox.getStyleClass().add("controls"); + controlsBox.getChildren().setAll( + new VBox(5, headerLabel, sizeDescLabel), + new Spacer(), + addButton + ); + controlsBox.setAlignment(CENTER_LEFT); + + var playlist = new ListView<>(model.playlist()); + playlist.setCellFactory(param -> new MediaCell(model)); + playlist.setPlaceholder(new Label("No Content")); + + getStyleClass().add("playlist"); + setSpacing(10); + getChildren().setAll(controlsBox, loadProgress, playlist); + + // ~ + + model.currentTrackProperty().addListener((obs, old, val) -> playlist.refresh()); + + model.playlist().addListener((ListChangeListener) c -> { + if (model.playlist().size() > 0) { + sizeLabel.setText(String.valueOf(model.playlist().size())); + sizeDescLabel.setGraphic(sizeLabel); + sizeDescLabel.setText("tracks"); + } else { + sizeDescLabel.setGraphic(null); + sizeDescLabel.setText("empty"); + } + }); + + model.backgroundColorProperty().addListener((obs, old, val) -> { + var color = model.backgroundColorProperty().get(); + playlist.setStyle("-color-cell-bg:" + toWebColor(color) + ";"); + setBackground(new Background(new BackgroundFill(color, CornerRadii.EMPTY, Insets.EMPTY))); + }); + + addButton.setOnAction(e -> { + FileChooser fileChooser = new FileChooser(); + fileChooser.getExtensionFilters().addAll(new ExtensionFilter("MP3 files (*.mp3)", "*.mp3")); + List files = fileChooser.showOpenMultipleDialog(getScene().getWindow()); + if (files == null || files.isEmpty()) { return; } + + loadProgress.setVisible(true); + final Task task = new Task<>() { + int progress = 0; + + @Override + public Void call() throws InterruptedException { + for (File file : files) { + Thread.sleep(500); // add artificial delay to demonstrate progress bar + Platform.runLater(() -> model.addFile(new MediaFile(file))); + progress++; + updateProgress(progress, files.size()); + } + return null; + } + }; + + task.setOnSucceeded(te -> loadProgress.setVisible(false)); + loadProgress.progressProperty().bind(task.progressProperty()); + new Thread(task).start(); + }); + } + + /////////////////////////////////////////////////////////////////////////// + + private static class MediaCell extends ListCell { + + private final Model model; + private final HBox root; + private final Rectangle coverImage; + private final Label titleLabel; + private final Label artistLabel; + private final FontIcon playMark; + + public MediaCell(Model model) { + this.model = model; + + coverImage = new Rectangle(0, 0, 32, 32); + coverImage.setArcWidth(10.0); + coverImage.setArcHeight(10.0); + + titleLabel = new Label(); + titleLabel.setMaxWidth(MAX_VALUE); + titleLabel.getStyleClass().add(TEXT_CAPTION); + + artistLabel = new Label(); + artistLabel.setMaxWidth(MAX_VALUE); + + var titleBox = new VBox(5, titleLabel, artistLabel); + titleBox.setAlignment(CENTER_LEFT); + HBox.setHgrow(titleBox, ALWAYS); + + playMark = new FontIcon(PLAYLIST_PLAY); + + root = new HBox(10, coverImage, titleBox, playMark); + root.setAlignment(CENTER_LEFT); + root.setOnMouseClicked(e -> { + if (getItem() != null) { model.play(getItem()); } + }); + } + + @Override + protected void updateItem(MediaFile mediaFile, boolean empty) { + super.updateItem(mediaFile, empty); + + if (empty || mediaFile == null) { + setGraphic(null); + coverImage.setFill(null); + titleLabel.setText(null); + artistLabel.setText(null); + } else { + setGraphic(root); + + playMark.setVisible(Objects.equals(mediaFile, model.currentTrackProperty().get())); + + mediaFile.readMetadata(metadata -> { + coverImage.setFill(new ImagePattern(metadata.image())); + titleLabel.setText(metadata.title()); + artistLabel.setText(metadata.artist()); + }); + } + } + } +} \ No newline at end of file diff --git a/sampler/src/main/java/atlantafx/sampler/page/showcase/musicplayer/Utils.java b/sampler/src/main/java/atlantafx/sampler/page/showcase/musicplayer/Utils.java new file mode 100644 index 0000000..844b379 --- /dev/null +++ b/sampler/src/main/java/atlantafx/sampler/page/showcase/musicplayer/Utils.java @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: MIT */ +package atlantafx.sampler.page.showcase.musicplayer; + +import javafx.embed.swing.SwingFXUtils; +import javafx.scene.image.Image; +import javafx.scene.image.WritableImage; +import javafx.scene.paint.Color; +import javafx.util.Duration; + +final class Utils { + + public static WritableImage copyImage(Image source) { + int height = (int) source.getHeight(); + int width = (int) source.getWidth(); + var reader = source.getPixelReader(); + var target = new WritableImage(width, height); + var writer = target.getPixelWriter(); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + Color color = reader.getColor(x, y); + writer.setColor(x, y, color); + } + } + return target; + } + + public static Color getDominantColor(Image image, double opacity) { + int[] dominant = ColorThief.getColor(SwingFXUtils.fromFXImage(image, null)); + if (dominant == null || dominant.length != 3) { return Color.TRANSPARENT; } + return Color.rgb(dominant[0], dominant[1], dominant[2], opacity); + } + + public static String formatDuration(Duration duration) { + long seconds = (long) duration.toSeconds(); + return seconds < 3600 ? + String.format("%02d:%02d", (seconds % 3600) / 60, seconds % 60) : + String.format("%d:%02d:%02d", seconds / 3600, (seconds % 3600) / 60, seconds % 60); + } + + public static String toWebColor(Color color) { + int r = ((int) Math.round(color.getRed() * 255)) << 24; + int g = ((int) Math.round(color.getGreen() * 255)) << 16; + int b = ((int) Math.round(color.getBlue() * 255)) << 8; + int a = ((int) Math.round(color.getOpacity() * 255)); + return String.format("#%08X", (r + g + b + a)); + } +} diff --git a/sampler/src/main/java/atlantafx/sampler/page/showcase/musicplayer/music-player.css b/sampler/src/main/java/atlantafx/sampler/page/showcase/musicplayer/music-player.css new file mode 100644 index 0000000..faec3ac --- /dev/null +++ b/sampler/src/main/java/atlantafx/sampler/page/showcase/musicplayer/music-player.css @@ -0,0 +1,26 @@ +/** SPDX-License-Identifier: MIT */ + +.music-player-showcase { + -fx-border-width: 1; + -fx-border-color: -color-border-default; +} + +.music-player-showcase .playlist { + -fx-padding: 10; +} + +.music-player-showcase .playlist > .list-view { + -fx-border-width: 0; +} +.music-player-showcase .playlist > .list-view .list-cell { + -fx-cell-size: 4em; + -fx-background-color: transparent; + -fx-background-radius: 6px; +} +.music-player-showcase .playlist > .list-view:focused>.virtual-flow>.clipped-container>.sheet>.list-cell:filled:selected { + -fx-background-color: transparent; +} +.music-player-showcase .playlist > .list-view:focused>.virtual-flow>.clipped-container>.sheet>.list-cell:selected:hover, +.music-player-showcase .playlist > .list-view:focused>.virtual-flow>.clipped-container>.sheet>.list-cell:filled:hover { + -fx-background-color: derive(-color-cell-bg, -5%); +} diff --git a/sampler/src/main/java/module-info.java b/sampler/src/main/java/module-info.java index f32f9a4..bf929ce 100755 --- a/sampler/src/main/java/module-info.java +++ b/sampler/src/main/java/module-info.java @@ -6,11 +6,13 @@ module atlantafx.sampler { requires java.desktop; requires javafx.swing; + requires javafx.media; requires javafx.web; requires org.kordamp.ikonli.core; requires org.kordamp.ikonli.javafx; requires org.kordamp.ikonli.feather; + requires org.kordamp.ikonli.material2; requires fr.brouillard.oss.cssfx; requires datafaker; diff --git a/sampler/src/main/resources/assets/styles/index.css b/sampler/src/main/resources/assets/styles/index.css index 8574e3c..6296622 100755 --- a/sampler/src/main/resources/assets/styles/index.css +++ b/sampler/src/main/resources/assets/styles/index.css @@ -3,6 +3,21 @@ .root { -fx-font-family: "Inter"; } +.root:showcase-mode #sidebar, +.root:showcase-mode .page > .header { + -fx-min-width: 0; + -fx-pref-width: 0; + -fx-max-width: 0; + -fx-min-height: 0; + -fx-pref-height: 0; + -fx-max-height: 0; + visibility: false; +} +.root:showcase-mode .page > .stack > .scroll-pane > .viewport > * > .wrapper > .user-content { + -fx-max-width: 4096px; + -fx-padding: 0; + -fx-spacing: 0; +} #sidebar { -fx-padding: 0 0 12px 0; diff --git a/sampler/src/main/resources/images/no-image.png b/sampler/src/main/resources/images/no-image.png new file mode 100644 index 0000000000000000000000000000000000000000..675443370a29e7ada39ad2b76a65ec92adeda047 GIT binary patch literal 21222 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 HftUUZnYbcK literal 0 HcmV?d00001 diff --git a/sampler/src/main/resources/images/vinyl.jpg b/sampler/src/main/resources/images/vinyl.jpg new file mode 100644 index 0000000000000000000000000000000000000000..360e5e232dd4575838cd581276fba4cb306da71d GIT binary patch literal 95946 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%0D`9%{v-2kz^aRZZG13o{=w=P^YuiOb2H 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