Improve showcases

This commit is contained in:
mkpaz 2023-05-20 15:43:47 +04:00
parent 38ce8e9446
commit c97142e9bf
63 changed files with 474 additions and 385 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

@ -18,7 +18,11 @@ See the <a href="https://mkpaz.github.io/atlantafx/">docs</a> for more info.
</b></p>
<p align="center">
<img src="https://raw.githubusercontent.com/mkpaz/atlantafx/master/.screenshots/demo.gif" alt="Logo"/><br/>
<img src="https://raw.githubusercontent.com/mkpaz/atlantafx/master/.screenshots/titlepage/blueprints.png" alt="blueprints"/><br/>
<img src="https://raw.githubusercontent.com/mkpaz/atlantafx/master/.screenshots/titlepage/fm.png" alt="apps"/><br/>
<img src="https://raw.githubusercontent.com/mkpaz/atlantafx/master/.screenshots/titlepage/mp.png" alt="apps"/><br/>
<img src="https://raw.githubusercontent.com/mkpaz/atlantafx/master/.screenshots/titlepage/page.png" alt="page"/><br/>
<img src="https://raw.githubusercontent.com/mkpaz/atlantafx/master/.screenshots/titlepage/themes.png" alt="themes"/><br/>
</p>
* Flat interface inspired by the variety of Web component frameworks.

@ -59,7 +59,7 @@
<maven.compiler.target>${java.version}</maven.compiler.target>
<java.version>17</java.version>
<openjfx.version>19</openjfx.version>
<openjfx.version>20</openjfx.version>
<sass.version>1.54.4</sass.version>
<app.name>AtlantaFX</app.name>

@ -2,7 +2,7 @@
package atlantafx.sampler.layout;
import atlantafx.sampler.util.Containers;
import atlantafx.sampler.util.NodeUtils;
import javafx.geometry.Insets;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.StackPane;
@ -21,7 +21,7 @@ public final class ApplicationWindow extends AnchorPane {
new Overlay(),
new MainLayer()
);
Containers.setAnchors(body, Insets.EMPTY);
NodeUtils.setAnchors(body, Insets.EMPTY);
getChildren().setAll(body);
}

@ -9,7 +9,7 @@ import atlantafx.sampler.Resources;
import atlantafx.sampler.event.DefaultEventBus;
import atlantafx.sampler.event.PageEvent;
import atlantafx.sampler.theme.HighlightJSTheme;
import atlantafx.sampler.util.Containers;
import atlantafx.sampler.util.NodeUtils;
import java.io.IOException;
import java.io.InputStream;
import javafx.geometry.Insets;
@ -48,7 +48,7 @@ final class CodeViewer extends AnchorPane {
private void lazyLoadWebView() {
if (webView == null) {
webView = new WebView();
Containers.setAnchors(webView, Insets.EMPTY);
NodeUtils.setAnchors(webView, Insets.EMPTY);
getChildren().add(0, webView);
}
}

@ -77,7 +77,8 @@ class MainLayer extends BorderPane {
}
try {
WritableImage img = page.getSnapshotTarget().snapshot(new SnapshotParameters(), null);
SnapshotParameters sp = new SnapshotParameters();
WritableImage img = page.getSnapshotTarget().snapshot(sp, null);
ImageIO.write(SwingFXUtils.fromFXImage(img, null), "png", file);
} catch (IOException ex) {
throw new RuntimeException(ex);

@ -48,7 +48,7 @@ import atlantafx.sampler.page.components.TreeTableViewPage;
import atlantafx.sampler.page.components.TreeViewPage;
import atlantafx.sampler.page.general.BBCodePage;
import atlantafx.sampler.page.general.IconsPage;
import atlantafx.sampler.page.showcase.OverviewPage;
import atlantafx.sampler.page.showcase.BlueprintsPage;
import atlantafx.sampler.page.general.ThemePage;
import atlantafx.sampler.page.general.TypographyPage;
import atlantafx.sampler.page.showcase.filemanager.FileManagerPage;
@ -214,7 +214,7 @@ public class MainModel {
var showcases = NavTree.Item.group("Showcase", new FontIcon(Material2OutlinedMZ.VISIBILITY));
showcases.getChildren().setAll(
NAV_TREE.get(OverviewPage.class),
NAV_TREE.get(BlueprintsPage.class),
NAV_TREE.get(FileManagerPage.class),
NAV_TREE.get(MusicPlayerPage.class)
);
@ -310,7 +310,7 @@ public class MainModel {
map.put(TreeViewPage.class, NavTree.Item.page(TreeViewPage.NAME, TreeViewPage.class));
// showcases
map.put(OverviewPage.class, NavTree.Item.page(OverviewPage.NAME, OverviewPage.class));
map.put(BlueprintsPage.class, NavTree.Item.page(BlueprintsPage.NAME, BlueprintsPage.class));
map.put(FileManagerPage.class, NavTree.Item.page(FileManagerPage.NAME, FileManagerPage.class));
map.put(MusicPlayerPage.class, NavTree.Item.page(MusicPlayerPage.NAME, MusicPlayerPage.class));

@ -3,7 +3,6 @@
package atlantafx.sampler.layout;
import atlantafx.sampler.util.Animations;
import atlantafx.sampler.util.Containers;
import atlantafx.sampler.util.NodeUtils;
import java.util.Objects;
import java.util.function.Consumer;
@ -51,7 +50,7 @@ public final class Overlay extends StackPane {
centerContentWrapper.setAlignment(Pos.CENTER);
scrollPane = new ScrollPane();
Containers.setScrollConstraints(scrollPane,
NodeUtils.setScrollConstraints(scrollPane,
ScrollPane.ScrollBarPolicy.AS_NEEDED, true,
ScrollPane.ScrollBarPolicy.NEVER, true
);
@ -108,11 +107,11 @@ public final class Overlay extends StackPane {
switch (pos) {
case LEFT -> {
edgeContentWrapper.getChildren().setAll(content);
Containers.setAnchors(content, new Insets(0, -1, 0, 0));
NodeUtils.setAnchors(content, new Insets(0, -1, 0, 0));
}
case RIGHT -> {
edgeContentWrapper.getChildren().setAll(content);
Containers.setAnchors(content, new Insets(0, 0, 0, -1));
NodeUtils.setAnchors(content, new Insets(0, 0, 0, -1));
}
case CENTER -> {
centerContentWrapper.getChildren().setAll(content);

@ -8,7 +8,6 @@ import static atlantafx.base.theme.Styles.FLAT;
import static atlantafx.base.theme.Styles.TITLE_4;
import atlantafx.base.controls.Spacer;
import atlantafx.sampler.util.Containers;
import java.util.Objects;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
@ -70,8 +69,10 @@ public abstract class OverlayDialog<T extends Region> extends VBox {
VBox.setVgrow(footerBox, Priority.NEVER);
// IMPORTANT: this guarantees client will use correct width and height
Containers.usePrefWidth(this);
Containers.usePrefHeight(this);
setMinWidth(USE_PREF_SIZE);
setMaxWidth(USE_PREF_SIZE);
setMinHeight(USE_PREF_SIZE);
setMaxHeight(USE_PREF_SIZE);
// if you're updating this line, update setContent() method as well
getChildren().setAll(headerBox, footerBox);

@ -2,12 +2,12 @@
package atlantafx.sampler.page;
import static atlantafx.sampler.util.Containers.setScrollConstraints;
import static javafx.scene.control.ScrollPane.ScrollBarPolicy.AS_NEEDED;
import static javafx.scene.control.ScrollPane.ScrollBarPolicy.NEVER;
import atlantafx.base.util.BBCodeParser;
import atlantafx.sampler.layout.Overlay;
import atlantafx.sampler.util.NodeUtils;
import java.net.URI;
import javafx.geometry.Pos;
import javafx.scene.Node;
@ -39,7 +39,7 @@ public abstract class AbstractPage extends StackPane implements Page {
userContent.setMaxWidth(Math.min(Page.MAX_WIDTH, 800));
var scrollPane = new ScrollPane(userContentArea);
setScrollConstraints(scrollPane, AS_NEEDED, true, NEVER, true);
NodeUtils.setScrollConstraints(scrollPane, AS_NEEDED, true, NEVER, true);
scrollPane.setMaxHeight(20_000);
getChildren().setAll(scrollPane);

@ -2,12 +2,12 @@
package atlantafx.sampler.page;
import static atlantafx.sampler.util.Containers.setScrollConstraints;
import static javafx.scene.control.ScrollPane.ScrollBarPolicy.AS_NEEDED;
import static javafx.scene.control.ScrollPane.ScrollBarPolicy.NEVER;
import atlantafx.base.theme.Styles;
import atlantafx.sampler.layout.Overlay;
import atlantafx.sampler.util.NodeUtils;
import java.net.URI;
import java.util.LinkedHashSet;
import java.util.Objects;
@ -58,7 +58,7 @@ public abstract class OutlinePage extends StackPane implements Page {
userContent.setMaxWidth(Page.MAX_WIDTH - OUTLINE_WIDTH - 100);
scrollPane.setContent(userContentArea);
setScrollConstraints(scrollPane, AS_NEEDED, true, NEVER, true);
NodeUtils.setScrollConstraints(scrollPane, AS_NEEDED, true, NEVER, true);
scrollPane.setMaxHeight(20_000);
// scroll spy

@ -10,7 +10,6 @@ import static atlantafx.sampler.util.ContrastLevel.getContrastRatioOpacityAware;
import static atlantafx.sampler.util.JColorUtils.flattenColor;
import atlantafx.base.theme.Styles;
import atlantafx.sampler.util.Containers;
import atlantafx.sampler.util.ContrastLevel;
import atlantafx.sampler.util.NodeUtils;
import java.util.function.Consumer;
@ -51,17 +50,17 @@ final class ColorPaletteBlock extends VBox {
contrastRatioText = new Text();
contrastRatioText.setStyle("-fx-fill:" + fgColorName + ";");
contrastRatioText.getStyleClass().addAll("contrast-ratio-text", TITLE_3);
Containers.setAnchors(contrastRatioText, new Insets(5, -1, -1, 5));
NodeUtils.setAnchors(contrastRatioText, new Insets(5, -1, -1, 5));
contrastLevelLabel.setGraphic(contrastLevelIcon);
contrastLevelLabel.getStyleClass().add("contrast-level-label");
contrastLevelLabel.setVisible(false);
Containers.setAnchors(contrastLevelLabel, new Insets(-1, 3, 3, -1));
NodeUtils.setAnchors(contrastLevelLabel, new Insets(-1, 3, 3, -1));
editIcon.setIconSize(24);
editIcon.getStyleClass().add("edit-icon");
NodeUtils.toggleVisibility(editIcon, false);
Containers.setAnchors(editIcon, new Insets(3, 3, -1, -1));
NodeUtils.setAnchors(editIcon, new Insets(3, 3, -1, -1));
colorRectangle = new AnchorPane();
colorRectangle.setStyle(

@ -14,7 +14,7 @@ import atlantafx.base.controls.Spacer;
import atlantafx.sampler.theme.SamplerTheme;
import atlantafx.sampler.theme.ThemeManager;
import atlantafx.sampler.theme.ThemeRepository;
import atlantafx.sampler.util.Containers;
import atlantafx.sampler.util.NodeUtils;
import java.io.File;
import java.util.Map;
import java.util.Objects;
@ -63,7 +63,7 @@ final class ThemeRepoManager extends VBox {
REPO.getAll().forEach(theme -> themeList.getChildren().add(themeCell(theme)));
var scrollPane = new ScrollPane(themeList);
Containers.setScrollConstraints(scrollPane, AS_NEEDED, true, AS_NEEDED, true);
NodeUtils.setScrollConstraints(scrollPane, AS_NEEDED, true, AS_NEEDED, true);
scrollPane.setMaxHeight(4000);
VBox.setVgrow(scrollPane, ALWAYS);

@ -2,11 +2,11 @@
package atlantafx.sampler.page.showcase;
import static atlantafx.sampler.util.Containers.setScrollConstraints;
import static javafx.scene.control.ScrollPane.ScrollBarPolicy.AS_NEEDED;
import atlantafx.sampler.Resources;
import atlantafx.sampler.page.Page;
import atlantafx.sampler.util.NodeUtils;
import java.io.IOException;
import java.net.URI;
import javafx.fxml.FXMLLoader;
@ -19,17 +19,17 @@ import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import org.jetbrains.annotations.Nullable;
public final class OverviewPage extends ScrollPane implements Page {
public final class BlueprintsPage extends ScrollPane implements Page {
public static final String NAME = "Overview";
private VBox wrapper;
public static final String NAME = "Blueprints";
private final VBox wrapper;
@Override
public String getName() {
return NAME;
}
public OverviewPage() {
public BlueprintsPage() {
super();
try {
@ -37,21 +37,21 @@ public final class OverviewPage extends ScrollPane implements Page {
wrapper.setAlignment(Pos.TOP_CENTER);
var loader = new FXMLLoader(
Resources.getResource("assets/fxml/overview/index.fxml").toURL()
Resources.getResource("assets/fxml/blueprints/index.fxml").toURL()
);
Parent fxmlContent = loader.load();
((Pane) fxmlContent).setMaxWidth(Page.MAX_WIDTH);
VBox.setVgrow(fxmlContent, Priority.ALWAYS);
wrapper.getChildren().setAll(fxmlContent);
setScrollConstraints(this, AS_NEEDED, true, AS_NEEDED, true);
NodeUtils.setScrollConstraints(this, AS_NEEDED, true, AS_NEEDED, true);
setMaxHeight(20_000);
setContent(wrapper);
} catch (IOException e) {
throw new RuntimeException("Unable to load FXML file", e);
}
setId("overview");
setId("blueprints");
}
@Override

@ -11,6 +11,7 @@ import javafx.css.PseudoClass;
import javafx.scene.Node;
import javafx.scene.control.Alert;
import javafx.scene.control.Label;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
@ -30,6 +31,7 @@ public abstract class ShowcasePage extends StackPane implements Page {
protected VBox showcaseWindow = new VBox();
protected Label windowTitle = new Label();
protected VBox showCaseContent = new VBox();
protected FontIcon aboutBtn = new FontIcon(Feather.HELP_CIRCLE);
protected int windowWidth = DEFAULT_WIDTH;
protected int windowHeight = DEFAULT_HEIGHT;
protected BooleanProperty maximized = new SimpleBooleanProperty();
@ -43,11 +45,15 @@ public abstract class ShowcasePage extends StackPane implements Page {
protected void createShowcaseLayout() {
windowTitle.getStyleClass().addAll("title");
aboutBtn.getStyleClass().addAll(Styles.SMALL, Styles.FLAT);
var maximizeBtn = new FontIcon(Feather.MAXIMIZE_2);
maximizeBtn.getStyleClass().addAll(Styles.SMALL, Styles.FLAT);
maximizeBtn.setOnMouseClicked(e -> maximized.set(!maximized.get()));
var windowHeader = new HBox(windowTitle, new Spacer(), maximizeBtn);
var windowHeader = new HBox(
20, windowTitle, new Spacer(), aboutBtn, maximizeBtn
);
windowHeader.getStyleClass().add("header");
showcaseWindow.getStyleClass().add("window");
@ -75,6 +81,18 @@ public abstract class ShowcasePage extends StackPane implements Page {
}
}
protected void setAboutInfo(String text) {
var tooltip = new Tooltip(text);
tooltip.setWrapText(true);
tooltip.setMaxWidth(300);
aboutBtn.setOnMouseEntered(e -> {
var localBounds = aboutBtn.getBoundsInLocal();
var screenBounds = aboutBtn.localToScreen(localBounds);
tooltip.show(getScene().getWindow(), screenBounds.getCenterX(), screenBounds.getMaxY());
});
aboutBtn.setOnMouseExited(e -> tooltip.hide());
}
protected void setShowCaseContent(Node node) {
setShowCaseContent(node, DEFAULT_WIDTH, DEFAULT_HEIGHT);
}

@ -1,9 +0,0 @@
/* SPDX-License-Identifier: MIT */
package atlantafx.sampler.page.showcase.filemanager;
import java.nio.file.Path;
import org.kordamp.ikonli.Ikon;
record Bookmark(String title, Path path, Ikon icon) {
}

@ -1,76 +0,0 @@
package atlantafx.sampler.page.showcase.filemanager;
import static atlantafx.sampler.page.showcase.filemanager.Model.USER_HOME;
import atlantafx.base.theme.Tweaks;
import java.nio.file.Files;
import java.nio.file.Path;
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;
final class BookmarkList extends ListView<Bookmark> {
public BookmarkList(Model model) {
getStyleClass().addAll("bookmark-list", Tweaks.EDGE_TO_EDGE);
// 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<Bookmark> {
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());
}
}
}
}

@ -0,0 +1,88 @@
/* SPDX-License-Identifier: MIT */
package atlantafx.sampler.page.showcase.filemanager;
import static atlantafx.sampler.page.showcase.filemanager.Utils.isFileHidden;
import atlantafx.base.theme.Tweaks;
import java.io.File;
import java.util.Comparator;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.image.ImageView;
final class DirectoryTree extends TreeView<File> {
@SuppressWarnings({"unchecked", "rawtypes"})
public DirectoryTree(Model model, File rootPath) {
super();
getStyleClass().add(Tweaks.ALT_ICON);
var root = new TreeItem<>(rootPath);
root.setExpanded(true);
setRoot(root);
// scan file tree two levels deep for starters
scan(root, 2);
setCellFactory(c -> new TreeCell<>() {
@Override
protected void updateItem(File item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
} else {
setText(item.getName());
var image = new ImageView(FileIconRepository.FOLDER);
image.setFitWidth(20);
image.setFitHeight(20);
setGraphic(image);
pseudoClassStateChanged(Model.HIDDEN, isFileHidden(item.toPath()));
}
}
});
// scan deeper as the user navigates down the tree
root.addEventHandler(TreeItem.branchExpandedEvent(), event -> {
TreeItem parent = event.getTreeItem();
parent.getChildren().forEach(child -> {
var item = (TreeItem<File>) child;
if (item.getChildren().isEmpty()) {
scan(item, 1);
}
});
});
getSelectionModel().selectedItemProperty().addListener((obs, old, item) -> {
if (item != null && item.getValue() != null) {
model.navigate(item.getValue().toPath(), true);
}
});
}
public static void scan(TreeItem<File> parent, int depth) {
File[] files = parent.getValue().listFiles();
depth--;
if (files != null) {
for (File f : files) {
if (f.isDirectory()) {
var item = new TreeItem<>(f);
parent.getChildren().add(item);
if (depth > 0) {
scan(item, depth);
}
}
}
parent.getChildren().sort(
Comparator.comparing(TreeItem::getValue)
);
}
}
}

@ -11,11 +11,13 @@ import javafx.scene.image.Image;
final class FileIconRepository {
private static final String IMAGE_DIRECTORY = "images/papirus/";
public static final Image UNKNOWN_FILE =
new Image(Resources.getResourceAsStream(IMAGE_DIRECTORY + "mimetypes/text-plain.png"));
public static final Image FOLDER =
new Image(Resources.getResourceAsStream(IMAGE_DIRECTORY + "places/folder-blue.png"));
public static final String IMAGE_DIRECTORY = "images/papirus/";
public static final Image UNKNOWN_FILE = new Image(
Resources.getResourceAsStream(IMAGE_DIRECTORY + "mimetypes/text-plain.png")
);
public static final Image FOLDER = new Image(
Resources.getResourceAsStream(IMAGE_DIRECTORY + "places/folder-paleorange.png")
);
private final Map<String, Image> cache = new HashMap<>();
private final Set<String> unknownMimeTypes = new HashSet<>();

@ -21,8 +21,9 @@ import javafx.scene.control.TableView;
final class FileList {
static final Comparator<Path> FILE_TYPE_COMPARATOR =
Comparator.comparing(path -> !Files.isDirectory(path));
static final Comparator<Path> FILE_TYPE_COMPARATOR = Comparator.comparing(
path -> !Files.isDirectory(path)
);
static final Predicate<Path> PREDICATE_ANY = path -> true;
static final Predicate<Path> PREDICATE_NOT_HIDDEN = path -> !isFileHidden(path);

@ -2,19 +2,16 @@
package atlantafx.sampler.page.showcase.filemanager;
import static atlantafx.base.theme.Styles.BUTTON_ICON;
import static atlantafx.base.theme.Styles.FLAT;
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;
import static atlantafx.sampler.util.Controls.iconButton;
import atlantafx.base.controls.Breadcrumbs;
import atlantafx.base.controls.Breadcrumbs.BreadCrumbItem;
import atlantafx.base.controls.Spacer;
import atlantafx.base.theme.Tweaks;
import atlantafx.base.theme.Styles;
import atlantafx.sampler.page.showcase.ShowcasePage;
import atlantafx.sampler.util.Containers;
import atlantafx.sampler.util.NodeUtils;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
@ -25,19 +22,22 @@ import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBase;
import javafx.scene.control.CheckMenuItem;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Hyperlink;
import javafx.scene.control.Label;
import javafx.scene.control.MenuButton;
import javafx.scene.control.MenuItem;
import javafx.scene.control.SplitPane;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToolBar;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.util.Callback;
import org.kordamp.ikonli.feather.Feather;
import org.kordamp.ikonli.javafx.FontIcon;
import org.kordamp.ikonli.material2.Material2AL;
import org.kordamp.ikonli.material2.Material2MZ;
import org.kordamp.ikonli.material2.Material2OutlinedAL;
public class FileManagerPage extends ShowcasePage {
@ -59,31 +59,40 @@ public class FileManagerPage extends ShowcasePage {
}
private void createView() {
var backBtn = iconButton(Feather.ARROW_LEFT, false);
var backBtn = new Button("", new FontIcon(Feather.ARROW_LEFT));
backBtn.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
backBtn.getStyleClass().add(Styles.BUTTON_ICON);
backBtn.setOnAction(e -> model.back());
backBtn.disableProperty().bind(model.getHistory().canGoBackProperty().not());
backBtn.setTooltip(new Tooltip("Back"));
var forthBtn = iconButton(Feather.ARROW_RIGHT, false);
var forthBtn = new Button("", new FontIcon(Feather.ARROW_RIGHT));
forthBtn.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
forthBtn.getStyleClass().add(Styles.BUTTON_ICON);
forthBtn.setOnAction(e -> model.forth());
forthBtn.disableProperty().bind(model.getHistory().canGoForthProperty().not());
forthBtn.setTooltip(new Tooltip("Forward"));
Breadcrumbs<Path> breadcrumbs = breadCrumbs();
Breadcrumbs<Path> breadcrumbs = createBreadcrumbs();
breadcrumbs.setMaxWidth(Double.MAX_VALUE);
HBox.setHgrow(breadcrumbs, Priority.ALWAYS);
breadcrumbs.setOnCrumbAction(e -> model.navigate(e.getSelectedCrumb().getValue(), true));
var createMenuBtn = new MenuButton();
createMenuBtn.setText("New");
createMenuBtn.getItems().setAll(
new MenuItem("New Folder"),
new MenuItem("New Document")
);
var createBtn = new Button(null, new FontIcon(Feather.FOLDER));
createBtn.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
createBtn.getStyleClass().add(Styles.BUTTON_ICON);
var toggleHiddenCheck = new CheckMenuItem("Show Hidden Files");
toggleHiddenCheck.setSelected(false);
var treeTgl = new ToggleButton("", new FontIcon(Material2OutlinedAL.ACCOUNT_TREE));
treeTgl.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
treeTgl.getStyleClass().add(Styles.BUTTON_ICON);
treeTgl.setSelected(true);
treeTgl.setTooltip(new Tooltip("Show tree"));
var menuBtn = new MenuButton();
menuBtn.setGraphic(new FontIcon(Material2MZ.MENU));
menuBtn.getItems().setAll(toggleHiddenCheck);
menuBtn.getStyleClass().addAll(BUTTON_ICON, Tweaks.NO_ARROW);
var hiddenTgl = new ToggleButton("", new FontIcon(Material2OutlinedAL.FILTER_LIST));
hiddenTgl.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
hiddenTgl.getStyleClass().add(Styles.BUTTON_ICON);
hiddenTgl.setSelected(true);
hiddenTgl.setTooltip(new Tooltip("Show hidden files"));
var topBar = new ToolBar();
topBar.getItems().setAll(
@ -91,18 +100,19 @@ public class FileManagerPage extends ShowcasePage {
forthBtn,
new Spacer(10),
breadcrumbs,
new Spacer(),
createMenuBtn,
menuBtn
new Spacer(10),
treeTgl,
hiddenTgl
);
// ~
var dirTree = new DirectoryTree(model, Model.USER_HOME.toFile());
dirTree.setMinWidth(100);
BookmarkList bookmarksList = new BookmarkList(model);
DirectoryView directoryView = new TableDirectoryView();
directoryView.setDirectory(model.currentPathProperty().get());
directoryView.setOnAction(path -> {
var dirView = new TableDirectoryView();
dirView.setMinWidth(300);
dirView.setDirectory(model.currentPathProperty().get());
dirView.setOnAction(path -> {
if (Files.isDirectory(path)) {
model.navigate(path, true);
} else {
@ -111,25 +121,32 @@ public class FileManagerPage extends ShowcasePage {
});
// ~
var splitPane = new SplitPane(bookmarksList, directoryView.getView());
var splitPane = new SplitPane(dirTree, dirView.getView());
splitPane.widthProperty().addListener(
// set sidebar width in pixels depending on split pane width
(obs, old, val) -> splitPane.setDividerPosition(0, 200 / splitPane.getWidth())
(obs, old, val) -> splitPane.setDividerPosition(0, 300 / splitPane.getWidth())
);
var root = new BorderPane();
root.getStyleClass().add("file-manager-showcase");
root.setId("file-manager-showcase");
root.getStylesheets().add(STYLESHEET_URL);
root.setTop(new VBox(topBar));
root.setCenter(splitPane);
//root.setBottom(creditsBox);
toggleHiddenCheck.selectedProperty().addListener((obs, old, val) -> directoryView.getFileList()
hiddenTgl.selectedProperty().addListener((obs, old, val) -> dirView.getFileList()
.predicateProperty()
.set(val ? PREDICATE_ANY : PREDICATE_NOT_HIDDEN)
.set(!val ? PREDICATE_ANY : PREDICATE_NOT_HIDDEN)
);
directoryView.getFileList().predicateProperty().set(PREDICATE_NOT_HIDDEN);
dirView.getFileList().predicateProperty().set(PREDICATE_NOT_HIDDEN);
treeTgl.selectedProperty().addListener((obs, old, val) -> {
if (val) {
splitPane.getItems().add(0, dirTree);
splitPane.setDividerPosition(0, 300 / splitPane.getWidth());
} else {
splitPane.getItems().remove(dirTree);
}
});
model.currentPathProperty().addListener((obs, old, val) -> {
if (!Files.isReadable(val)) {
@ -141,25 +158,30 @@ public class FileManagerPage extends ShowcasePage {
breadcrumbs.setSelectedCrumb(
Breadcrumbs.buildTreeModel(getParentPath(val, 4).toArray(Path[]::new))
);
directoryView.setDirectory(val);
dirView.setDirectory(val);
});
setWindowTitle("File Manager", new FontIcon(Material2AL.FOLDER));
setShowCaseContent(root);
setAboutInfo("""
This is a simple file manager. You can navigate through \
the file system and open files using the default system application."""
);
setShowCaseContent(root, MAX_WIDTH, 600);
Containers.setAnchors(root, Insets.EMPTY);
NodeUtils.setAnchors(root, Insets.EMPTY);
}
private Breadcrumbs<Path> breadCrumbs() {
private Breadcrumbs<Path> createBreadcrumbs() {
Callback<BreadCrumbItem<Path>, ButtonBase> crumbFactory = crumb -> {
var btn = new Button(crumb.getValue().getFileName().toString());
btn.getStyleClass().add(FLAT);
btn.setFocusTraversable(false);
return btn;
var hyperlink = new Hyperlink(crumb.getValue().getFileName().toString());
hyperlink.setFocusTraversable(false);
return hyperlink;
};
Callback<BreadCrumbItem<Path>, ? extends Node> dividerFactory = item ->
item != null && !item.isLast() ? new Label("", new FontIcon(Material2AL.CHEVRON_RIGHT)) : null;
item != null && !item.isLast()
? new Label("", new FontIcon(Material2AL.CHEVRON_RIGHT))
: null;
var breadcrumbs = new Breadcrumbs<Path>();
breadcrumbs.setAutoNavigationEnabled(false);

@ -7,9 +7,12 @@ import java.nio.file.Paths;
import java.util.Objects;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.css.PseudoClass;
final class Model {
public static final PseudoClass HIDDEN = PseudoClass.getPseudoClass("hidden");
public static final PseudoClass FOLDER = PseudoClass.getPseudoClass("folder");
public static final Path USER_HOME = Paths.get(System.getProperty("user.home"));
private final ReadOnlyObjectWrapper<Path> currentPath = new ReadOnlyObjectWrapper<>();

@ -17,9 +17,11 @@ final class NavigationHistory {
private final IntegerProperty cursor = new SimpleIntegerProperty(0);
private final List<Path> history = new ArrayList<>();
private final BooleanBinding canGoBack = Bindings.createBooleanBinding(
() -> cursor.get() > 0 && history.size() > 1, cursor);
() -> cursor.get() > 0 && history.size() > 1, cursor
);
private final BooleanBinding canGoForth = Bindings.createBooleanBinding(
() -> cursor.get() < history.size() - 1, cursor);
() -> cursor.get() < history.size() - 1, cursor
);
public void append(Path path) {
if (path == null) {

@ -5,6 +5,8 @@ package atlantafx.sampler.page.showcase.filemanager;
import static javafx.scene.input.KeyCombination.CONTROL_DOWN;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.CustomMenuItem;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.control.SeparatorMenuItem;
import javafx.scene.input.KeyCode;
@ -18,31 +20,50 @@ final class RightClickMenu extends ContextMenu {
}
private void createMenu() {
var open = new MenuItem("Open");
var openItem = new MenuItem("Open");
var cut = new MenuItem("Cut");
cut.setAccelerator(new KeyCodeCombination(KeyCode.X, CONTROL_DOWN));
var cutItem = new MenuItem("Cut");
cutItem.setAccelerator(new KeyCodeCombination(KeyCode.X, CONTROL_DOWN));
var copy = new MenuItem("Copy");
copy.setAccelerator(new KeyCodeCombination(KeyCode.C, CONTROL_DOWN));
var copyItem = new MenuItem("Copy");
copyItem.setAccelerator(new KeyCodeCombination(KeyCode.C, CONTROL_DOWN));
var rename = new MenuItem("Rename");
rename.setAccelerator(new KeyCodeCombination(KeyCode.F2));
var renameItem = new MenuItem("Rename");
renameItem.setAccelerator(new KeyCodeCombination(KeyCode.F2));
var compress = new MenuItem("Compress");
var compressItem = new MenuItem("Compress");
var properties = new MenuItem("Properties");
var propsItem = new MenuItem("Properties");
getItems().setAll(
open,
new DemoMenuItem(),
new SeparatorMenuItem(),
cut,
copy,
rename,
openItem,
new SeparatorMenuItem(),
compress,
cutItem,
copyItem,
renameItem,
new SeparatorMenuItem(),
properties
compressItem,
new SeparatorMenuItem(),
propsItem
);
}
///////////////////////////////////////////////////////////////////////////
private static class DemoMenuItem extends CustomMenuItem {
public DemoMenuItem() {
super();
var label = new Label("This is a demo menu. None of \nthe options below are working.");
label.setWrapText(true);
label.setStyle("-fx-text-fill:-color-fg-muted");
setContent(label);
setHideOnClick(false);
getStyleClass().add("demo-menu-item");
}
}
}

@ -11,8 +11,8 @@ import static javafx.scene.control.TableColumn.SortType.ASCENDING;
import atlantafx.base.theme.Styles;
import atlantafx.base.theme.Tweaks;
import atlantafx.sampler.util.Containers;
import atlantafx.sampler.util.HumanReadableFormat;
import atlantafx.sampler.util.NodeUtils;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.FileTime;
@ -23,7 +23,6 @@ import java.util.stream.Stream;
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;
@ -35,8 +34,6 @@ import javafx.scene.layout.Pane;
final class TableDirectoryView extends AnchorPane implements DirectoryView {
private static final PseudoClass HIDDEN = PseudoClass.getPseudoClass("hidden");
private static final PseudoClass FOLDER = PseudoClass.getPseudoClass("folder");
private static final FileIconRepository REPO = new FileIconRepository();
private static final String UNKNOWN = "unknown";
@ -49,7 +46,7 @@ final class TableDirectoryView extends AnchorPane implements DirectoryView {
getChildren().setAll(table);
getStyleClass().addAll("table-directory-view");
Containers.setAnchors(table, Insets.EMPTY);
NodeUtils.setAnchors(table, Insets.EMPTY);
}
@SuppressWarnings("unchecked")
@ -152,8 +149,8 @@ final class TableDirectoryView extends AnchorPane implements DirectoryView {
imageView.setImage(FileIconRepository.FOLDER);
}
pseudoClassStateChanged(FOLDER, isDirectory);
getTableRow().pseudoClassStateChanged(HIDDEN, isFileHidden(path));
pseudoClassStateChanged(Model.FOLDER, isDirectory);
getTableRow().pseudoClassStateChanged(Model.HIDDEN, isFileHidden(path));
setGraphic(imageView);
setText(filename);
@ -195,10 +192,12 @@ final class TableDirectoryView extends AnchorPane implements DirectoryView {
if (empty) {
setText(null);
} else {
setText(fileTime != null
? HumanReadableFormat.date(fileTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime())
: UNKNOWN
);
if (fileTime == null) {
setText(UNKNOWN);
return;
}
var instant = fileTime.toInstant().atZone(ZoneId.systemDefault());
setText(HumanReadableFormat.date(instant.toLocalDateTime()));
}
}
}

@ -1,21 +1,18 @@
/* SPDX-License-Identifier: MIT */
.file-manager-showcase .bookmark-list .ikonli-font-icon {
-fx-icon-size: 18px;
}
.file-manager-showcase .breadcrumbs >.divider {
-fx-padding: 0;
}
.file-manager-showcase .table-directory-view .table-view {
-color-header-bg: -color-bg-default;
-color-cell-bg-selected: -color-accent-emphasis;
-color-cell-fg-selected: -color-fg-emphasis;
#file-manager-showcase .table-directory-view .table-view {
-color-header-bg: -color-bg-default;
}
/* 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:hidden >.table-cell > *,
#file-manager-showcase .tree-cell:hidden > * {
-fx-opacity: 0.75;
}
.demo-menu-item:hover,
.demo-menu-item:focused,
.demo-menu-item:pressed {
-fx-background-color: transparent;
}

@ -34,8 +34,9 @@ final class ColorThief {
public static int[] getColor(BufferedImage source) {
int[][] palette = getPalette(source, 5);
if (palette == null)
if (palette == null) {
return null;
}
return palette[0];
}
@ -43,8 +44,9 @@ final class ColorThief {
public static int[][] getPalette(BufferedImage source, int colorCount) {
MMCQ.ColorMap colorMap = getColorMap(source, colorCount);
if (colorMap == null)
if (colorMap == null) {
return null;
}
return colorMap.palette();
}
@ -55,10 +57,12 @@ final class ColorThief {
public static MMCQ.ColorMap getColorMap(BufferedImage sourceImage, int colorCount, int quality,
boolean ignoreWhite) {
if (colorCount < 2 || colorCount > 256)
if (colorCount < 2 || colorCount > 256) {
throw new IllegalArgumentException("Specified colorCount must be between 2 and 256.");
if (quality < 1)
}
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);
@ -69,9 +73,9 @@ final class ColorThief {
}
private static int[][] getPixelsFast(
BufferedImage sourceImage,
int quality,
boolean ignoreWhite) {
BufferedImage sourceImage,
int quality,
boolean ignoreWhite) {
DataBufferByte imageData = (DataBufferByte) sourceImage.getRaster().getDataBuffer();
byte[] pixels = imageData.getData();
int pixelCount = sourceImage.getWidth() * sourceImage.getHeight();
@ -87,7 +91,7 @@ final class ColorThief {
int expectedDataLength = pixelCount * colorDepth;
if (expectedDataLength != pixels.length) {
throw new IllegalArgumentException(
"(expectedDataLength = " + expectedDataLength + ") != (pixels.length = " + pixels.length + ")"
"(expectedDataLength = " + expectedDataLength + ") != (pixels.length = " + pixels.length + ")"
);
}
@ -106,7 +110,7 @@ final class ColorThief {
r = pixels[offset + 2] & 0xFF;
if (!(ignoreWhite && r > 250 && g > 250 && b > 250)) {
pixelArray[numUsedPixels] = new int[]{r, g, b};
pixelArray[numUsedPixels] = new int[] {r, g, b};
numUsedPixels++;
}
}
@ -120,7 +124,7 @@ final class ColorThief {
r = pixels[offset + 3] & 0xFF;
if (a >= 125 && !(ignoreWhite && r > 250 && g > 250 && b > 250)) {
pixelArray[numUsedPixels] = new int[]{r, g, b};
pixelArray[numUsedPixels] = new int[] {r, g, b};
numUsedPixels++;
}
}
@ -132,9 +136,9 @@ final class ColorThief {
}
private static int[][] getPixelsSlow(
BufferedImage sourceImage,
int quality,
boolean ignoreWhite) {
BufferedImage sourceImage,
int quality,
boolean ignoreWhite) {
int width = sourceImage.getWidth();
int height = sourceImage.getHeight();
@ -154,7 +158,7 @@ final class ColorThief {
g = (rgb >> 8) & 0xFF;
b = rgb & 0xFF;
if (!(ignoreWhite && r > 250 && g > 250 && b > 250)) {
res[numUsedPixels] = new int[]{r, g, b};
res[numUsedPixels] = new int[] {r, g, b};
numUsedPixels++;
}
}
@ -202,7 +206,9 @@ final class ColorThief {
@Override
public String toString() {
return "r1: " + r1 + " / r2: " + r2 + " / g1: " + g1 + " / g2: " + g2 + " / b1: " + b1 + " / b2: " + b2;
return "r1: " + r1 + " / r2: " + r2
+ " / g1: " + g1 + " / g2: " + g2
+ " / b1: " + b1 + " / b2: " + b2;
}
public int volume(boolean force) {
@ -260,10 +266,15 @@ final class ColorThief {
}
if (ntot > 0) {
gAvg = new int[]{(rsum / ntot), (gsum / ntot), (bsum / ntot)};
gAvg = new int[] {
(rsum / ntot), (gsum / ntot), (bsum / ntot)
};
} else {
gAvg = new int[]{(MULT * (r1 + r2 + 1) / 2), (MULT * (g1 + g2 + 1) / 2),
(MULT * (b1 + b2 + 1) / 2)};
gAvg = new int[] {
(MULT * (r1 + r2 + 1) / 2),
(MULT * (g1 + g2 + 1) / 2),
(MULT * (b1 + b2 + 1) / 2)
};
}
}
@ -318,8 +329,8 @@ final class ColorThief {
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)
+ Math.pow(color[1] - vbColor[1], 2)
+ Math.pow(color[2] - vbColor[2], 2)
);
if (d2 < d1) {
d1 = d2;
@ -387,7 +398,7 @@ final class ColorThief {
}
if (vbox.count(false) == 1) {
return new VBox[]{vbox.clone(), null};
return new VBox[] {vbox.clone(), null};
}
int rw = vbox.r2 - vbox.r1 + 1;
@ -447,16 +458,16 @@ final class ColorThief {
}
return maxw == rw ? doCut('r', vbox, partialSum, lookAheadSum, total)
: maxw == gw ? doCut('g', vbox, partialSum, lookAheadSum, total)
: doCut('b', 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
char color,
VBox vbox,
int[] partialSum,
int[] lookAheadSum,
int total
) {
int vboxDim1;
int vboxDim2;
@ -509,7 +520,7 @@ final class ColorThief {
vbox2.b1 = d2 + 1;
}
return new VBox[]{vbox1, vbox2};
return new VBox[] {vbox1, vbox2};
}
}

@ -26,6 +26,7 @@ record MediaFile(File file) {
// is costly and that instance is not even reusable.
public void readMetadata(Consumer<Metadata> callback) {
var media = new Media(file.toURI().toString());
System.out.println(file.toURI().toString());
var mediaPlayer = new MediaPlayer(media);
// The media information is obtained asynchronously and so not necessarily

@ -2,6 +2,10 @@
package atlantafx.sampler.page.showcase.musicplayer;
import atlantafx.sampler.Resources;
import java.io.File;
import java.nio.file.Paths;
import java.util.List;
import java.util.Objects;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ReadOnlyBooleanProperty;
@ -14,6 +18,11 @@ import javafx.scene.paint.Color;
final class Model {
private static final List<File> DEMO_FILES = List.of(
Paths.get(Resources.getResource("media/Beat Thee.mp3")).toFile(),
Paths.get(Resources.getResource("media/Study and Relax.mp3")).toFile()
);
private final ObservableList<MediaFile> playlist = FXCollections.observableArrayList();
private final ReadOnlyBooleanWrapper canGoBack = new ReadOnlyBooleanWrapper();
private final ReadOnlyBooleanWrapper canGoForward = new ReadOnlyBooleanWrapper();
@ -104,4 +113,9 @@ final class Model {
reset();
playlist().clear();
}
public void playDemo() {
DEMO_FILES.forEach(f -> addFile(new MediaFile(f)));
play(new MediaFile(DEMO_FILES.get(0)));
}
}

@ -14,8 +14,8 @@ public class MusicPlayerPage extends ShowcasePage {
public static final String NAME = "Music Player";
public static final Set<String> SUPPORTED_MEDIA_TYPES = Set.of("mp3");
private static final String STYLESHEET_URL = Objects.requireNonNull(
MusicPlayerPage.class.getResource("music-player.css")).toExternalForm();
private static final String STYLESHEET_URL =
Objects.requireNonNull(MusicPlayerPage.class.getResource("music-player.css")).toExternalForm();
@Override
public String getName() {
@ -34,10 +34,9 @@ public class MusicPlayerPage extends ShowcasePage {
var playerScreen = new PlayerScreen(model);
var root = new BorderPane();
root.setId("music-player-showcase");
root.setCenter(startScreen);
root.getStylesheets().add(STYLESHEET_URL);
root.getStyleClass().add("music-player-showcase");
model.playlist().addListener((ListChangeListener<MediaFile>) c -> {
if (model.playlist().size() > 0) {
@ -48,6 +47,9 @@ public class MusicPlayerPage extends ShowcasePage {
});
setWindowTitle("Music Player", new FontIcon(Feather.MUSIC));
setAboutInfo("""
Simple music player that able to play MP3 files. Inspired by ©Amberol."""
);
setShowCaseContent(root);
}

@ -184,9 +184,22 @@ final class PlayerPane extends VBox {
setAlignment(CENTER);
setSpacing(5);
setMinWidth(300);
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));
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)
);
}
private void init() {

@ -31,20 +31,29 @@ final class PlayerScreen extends SplitPane {
var domColor = Objects.equals(Color.TRANSPARENT, val)
? Color.TRANSPARENT
: Color.color(val.getRed(), val.getGreen(), val.getBlue(), 1);
var domColor10 = Objects.equals(Color.TRANSPARENT, val)
? Color.TRANSPARENT
: Color.color(val.getRed(), val.getGreen(), val.getBlue(), 0.1);
var domColor20 = Objects.equals(Color.TRANSPARENT, val)
? Color.TRANSPARENT
: Color.color(val.getRed(), val.getGreen(), val.getBlue(), 0.2);
var domColor50 = Objects.equals(Color.TRANSPARENT, val)
? Color.TRANSPARENT
: Color.color(val.getRed(), val.getGreen(), val.getBlue(), 0.5);
var domColor70 = Objects.equals(Color.TRANSPARENT, val)
? Color.TRANSPARENT
: Color.color(val.getRed(), val.getGreen(), val.getBlue(), 0.7);
setStyle("-color-dominant:" + toHexWithAlpha(domColor) + ";"
+ "-color-dominant-10:" + toHexWithAlpha(domColor10) + ";"
+ "-color-dominant-20:" + toHexWithAlpha(domColor20) + ";"
+ "-color-dominant-50:" + toHexWithAlpha(domColor50) + ";"
+ "-color-dominant-70:" + toHexWithAlpha(domColor70) + ";"
+ "-color-dominant-border:" + toHexWithAlpha(domColor70) + ";"
+ "-color-dominant-border:" + toHexWithAlpha(domColor50) + ";"
);
});
}

@ -37,8 +37,6 @@ final class StartScreen extends BorderPane {
}
private void createView() {
var jumboIcon = new FontIcon(Feather.MUSIC);
var noteText = new TextFlow(new Text(
"Select a file or a folder."
));
@ -54,10 +52,17 @@ final class StartScreen extends BorderPane {
addFileBtn.setPrefWidth(150);
addFileBtn.setOnAction(e -> addFile());
var controls = new VBox(10, addFolderBtn, addFileBtn);
var addDemoBtn = new Button("Play Demo");
addDemoBtn.getStyleClass().add(Styles.SUCCESS);
addDemoBtn.setPrefWidth(150);
addDemoBtn.setOnAction(e -> model.playDemo());
var controls = new VBox(10, addFolderBtn, addFileBtn, addDemoBtn);
controls.setAlignment(Pos.CENTER);
controls.setFillWidth(true);
var jumboIcon = new FontIcon(Feather.MUSIC);
var content = new VBox(30, jumboIcon, noteText, controls);
content.getStyleClass().add("content");
content.setAlignment(Pos.CENTER);

@ -1,50 +1,76 @@
/** SPDX-License-Identifier: MIT */
.music-player-showcase > .start-screen {
-fx-border-color: -color-border-muted;
-fx-border-width: 1;
}
.music-player-showcase > .start-screen > .content > .ikonli-font-icon {
#music-player-showcase > .start-screen > .content > .ikonli-font-icon {
-fx-icon-color: -color-fg-muted;
-fx-fill: -color-fg-muted;
-fx-icon-size: 64px;
-fx-fill: -color-fg-muted;
-fx-icon-size: 64px;
}
.music-player-showcase > .player-screen {
-fx-border-color: -color-border-muted;
-fx-border-width: 1;
-color-dominant: transparent;
-color-dominant-20: transparent;
-color-dominant-50: transparent;
-color-dominant-70: transparent;
-color-dominant-border: -color-border-muted;
#music-player-showcase > .player-screen {
/* default colors (when no artwork found) */
-color-dominant: transparent;
-color-dominant-10: transparent;
-color-dominant-20: transparent;
-color-dominant-50: transparent;
-color-dominant-70: transparent;
-color-dominant-border: -color-border-default;
-fx-background-color: radial-gradient(radius 100%, -color-dominant-20, -color-dominant-50);
}
.music-player-showcase > .player-screen > * > .player {
-fx-background-color: radial-gradient(radius 100%, -color-dominant-50, -color-dominant-20);
}
.music-player-showcase > .player-screen > * > .player > .media-controls > .play > .ikonli-font-icon {
-fx-icon-size: 32px;
}
.music-player-showcase > .player-screen > .split-pane-divider {
#music-player-showcase > .player-screen > .split-pane-divider {
-color-split-divider: -color-dominant-border;
-color-split-grabber: -color-dominant-border;
}
.music-player-showcase > .player-screen > * > .playlist {
-fx-background-color: radial-gradient(radius 100%, -color-dominant-20, -color-dominant-50);
/* == PLAYER == */
#music-player-showcase .player > .media-controls > .play > .ikonli-font-icon {
-fx-icon-size: 32px;
}
.music-player-showcase > .player-screen > * > .playlist > .controls {
#music-player-showcase .player .button {
-color-button-bg: -color-dominant-10;
-color-button-bg-hover: -color-dominant-20;
-color-button-bg-focused: -color-dominant-10;
-color-button-bg-pressed: -color-dominant-10;
-color-button-border: transparent;
-color-button-border-hover: transparent;
-color-button-border-focused: transparent;
-color-button-border-pressed: transparent;
}
#music-player-showcase .player .slider {
-color-slider-track: -color-dominant-50;
}
/* == PLAYLIST == */
#music-player-showcase .playlist > .controls {
-fx-border-color: -color-dominant-border;
-fx-border-width: 0 0 0.75px 0;
}
.music-player-showcase > .player-screen > * > .playlist > .controls > .button {
-color-button-bg-hover: transparent;
-color-button-bg-focused: transparent;
-color-button-bg-pressed: transparent;
#music-player-showcase .playlist > .controls > .button {
-color-button-bg-hover: -color-dominant-20;
-color-button-bg-focused: transparent;
-color-button-bg-pressed: transparent;
-color-button-border: transparent;
-color-button-border-hover: transparent;
-color-button-border-focused: transparent;
-color-button-border-pressed: transparent;
}
.music-player-showcase > .player-screen > * > .playlist > .list-view .list-cell {
-color-cell-bg: transparent;
-color-cell-bg-selected: transparent;
-color-cell-border: transparent;
#music-player-showcase .playlist .list-cell {
-color-cell-bg: transparent;
-color-cell-bg-selected: transparent;
-color-cell-bg-selected-focused: transparent;
-color-cell-border: transparent;
-fx-cell-size: 4em;
}
#music-player-showcase .playlist .list-cell:filled:hover {
-color-cell-bg: -color-dominant-20;
-color-cell-bg-selected: -color-dominant-20;
-color-cell-bg-selected-focused: -color-dominant-20;
}

@ -1,65 +0,0 @@
/* SPDX-License-Identifier: MIT */
package atlantafx.sampler.util;
import static javafx.scene.layout.Region.USE_COMPUTED_SIZE;
import static javafx.scene.layout.Region.USE_PREF_SIZE;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
@Deprecated
public final class Containers {
public static final ColumnConstraints H_GROW_NEVER = columnConstraints(Priority.NEVER);
public static void setAnchors(Node node, Insets insets) {
if (insets.getTop() >= 0) {
AnchorPane.setTopAnchor(node, insets.getTop());
}
if (insets.getRight() >= 0) {
AnchorPane.setRightAnchor(node, insets.getRight());
}
if (insets.getBottom() >= 0) {
AnchorPane.setBottomAnchor(node, insets.getBottom());
}
if (insets.getLeft() >= 0) {
AnchorPane.setLeftAnchor(node, insets.getLeft());
}
}
public static void setScrollConstraints(ScrollPane scrollPane,
ScrollPane.ScrollBarPolicy vbarPolicy, boolean fitHeight,
ScrollPane.ScrollBarPolicy hbarPolicy, boolean fitWidth) {
scrollPane.setVbarPolicy(vbarPolicy);
scrollPane.setFitToHeight(fitHeight);
scrollPane.setHbarPolicy(hbarPolicy);
scrollPane.setFitToWidth(fitWidth);
}
public static ColumnConstraints columnConstraints(Priority hgrow) {
return columnConstraints(USE_COMPUTED_SIZE, hgrow);
}
public static ColumnConstraints columnConstraints(double minWidth, Priority hgrow) {
double maxWidth = hgrow == Priority.ALWAYS ? Double.MAX_VALUE : USE_PREF_SIZE;
ColumnConstraints constraints = new ColumnConstraints(minWidth, USE_COMPUTED_SIZE, maxWidth);
constraints.setHgrow(hgrow);
return constraints;
}
public static void usePrefWidth(Region region) {
region.setMinWidth(USE_PREF_SIZE);
region.setMaxWidth(USE_PREF_SIZE);
}
public static void usePrefHeight(Region region) {
region.setMinHeight(USE_PREF_SIZE);
region.setMaxHeight(USE_PREF_SIZE);
}
}

@ -1,27 +0,0 @@
/* SPDX-License-Identifier: MIT */
package atlantafx.sampler.util;
import static atlantafx.base.theme.Styles.BUTTON_ICON;
import javafx.scene.control.Button;
import org.kordamp.ikonli.Ikon;
import org.kordamp.ikonli.javafx.FontIcon;
@Deprecated
public final class Controls {
public static Button iconButton(Ikon icon, boolean disable) {
return button("", icon, disable, BUTTON_ICON);
}
public static Button button(String text, Ikon icon, boolean disable, String... styleClasses) {
var button = new Button(text);
if (icon != null) {
button.setGraphic(new FontIcon(icon));
}
button.setDisable(disable);
button.getStyleClass().addAll(styleClasses);
return button;
}
}

@ -3,10 +3,13 @@
package atlantafx.sampler.util;
import java.util.List;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.control.ScrollPane;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
public final class NodeUtils {
@ -15,6 +18,30 @@ public final class NodeUtils {
node.setManaged(on);
}
public static void setAnchors(Node node, Insets insets) {
if (insets.getTop() >= 0) {
AnchorPane.setTopAnchor(node, insets.getTop());
}
if (insets.getRight() >= 0) {
AnchorPane.setRightAnchor(node, insets.getRight());
}
if (insets.getBottom() >= 0) {
AnchorPane.setBottomAnchor(node, insets.getBottom());
}
if (insets.getLeft() >= 0) {
AnchorPane.setLeftAnchor(node, insets.getLeft());
}
}
public static void setScrollConstraints(ScrollPane scrollPane,
ScrollPane.ScrollBarPolicy vbarPolicy, boolean fitHeight,
ScrollPane.ScrollBarPolicy hbarPolicy, boolean fitWidth) {
scrollPane.setVbarPolicy(vbarPolicy);
scrollPane.setFitToHeight(fitHeight);
scrollPane.setHbarPolicy(hbarPolicy);
scrollPane.setFitToWidth(fitWidth);
}
public static boolean isDoubleClick(MouseEvent e) {
return e.getButton().equals(MouseButton.PRIMARY) && e.getClickCount() == 2;
}

@ -1,6 +1,7 @@
// SPDX-License-Identifier: MIT
.showcase-page {
-fx-background-color: -color-bg-default;
>.window {
-fx-border-color: -color-accent-emphasis;
@ -44,7 +45,7 @@
}
}
#overview {
#blueprints {
-fx-background-color: -color-bg-inset;
.sample {

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB