Improve showcases
BIN
.screenshots/titlepage/blueprints.png
Normal file
After Width: | Height: | Size: 195 KiB |
BIN
.screenshots/titlepage/fm.png
Normal file
After Width: | Height: | Size: 81 KiB |
BIN
.screenshots/titlepage/mp.png
Normal file
After Width: | Height: | Size: 195 KiB |
BIN
.screenshots/titlepage/page.png
Normal file
After Width: | Height: | Size: 106 KiB |
BIN
.screenshots/titlepage/themes.png
Normal file
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.
|
||||
|
2
pom.xml
@ -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;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 128 KiB After Width: | Height: | Size: 128 KiB |
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 8.2 KiB |
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 88 KiB |
Before Width: | Height: | Size: 127 KiB After Width: | Height: | Size: 127 KiB |
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
@ -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 {
|
||||
|
BIN
sampler/src/main/resources/atlantafx/sampler/images/papirus/places/folder-paleorange.png
Normal file
After Width: | Height: | Size: 2.8 KiB |