From 38ce8e9446a0aecfcd179e77e47db22fae66cc84 Mon Sep 17 00:00:00 2001 From: mkpaz Date: Fri, 19 May 2023 22:52:54 +0400 Subject: [PATCH] Support saving any page snapshot to a file --- .../main/java/atlantafx/sampler/Launcher.java | 3 +- .../atlantafx/sampler/layout/MainLayer.java | 42 +++++++++++++++++++ .../atlantafx/sampler/page/AbstractPage.java | 7 +++- .../atlantafx/sampler/page/OutlinePage.java | 7 +++- .../java/atlantafx/sampler/page/Page.java | 3 ++ .../sampler/page/showcase/OverviewPage.java | 9 +++- .../sampler/page/showcase/ShowcasePage.java | 5 +++ 7 files changed, 72 insertions(+), 4 deletions(-) diff --git a/sampler/src/main/java/atlantafx/sampler/Launcher.java b/sampler/src/main/java/atlantafx/sampler/Launcher.java index 2b752e6..36ad04a 100755 --- a/sampler/src/main/java/atlantafx/sampler/Launcher.java +++ b/sampler/src/main/java/atlantafx/sampler/Launcher.java @@ -40,7 +40,8 @@ public class Launcher extends Application { public static final List SUPPORTED_HOTKEYS = List.of( new KeyCodeCombination(KeyCode.SLASH), - new KeyCodeCombination(KeyCode.T, KeyCombination.CONTROL_DOWN) + new KeyCodeCombination(KeyCode.T, KeyCombination.CONTROL_DOWN), + new KeyCodeCombination(KeyCode.W, KeyCombination.CONTROL_DOWN) ); public static void main(String[] args) { diff --git a/sampler/src/main/java/atlantafx/sampler/layout/MainLayer.java b/sampler/src/main/java/atlantafx/sampler/layout/MainLayer.java index 8bd7414..f554691 100644 --- a/sampler/src/main/java/atlantafx/sampler/layout/MainLayer.java +++ b/sampler/src/main/java/atlantafx/sampler/layout/MainLayer.java @@ -6,20 +6,30 @@ import static atlantafx.sampler.event.PageEvent.Action; import static javafx.scene.layout.Priority.ALWAYS; import atlantafx.sampler.event.DefaultEventBus; +import atlantafx.sampler.event.HotkeyEvent; import atlantafx.sampler.event.PageEvent; import atlantafx.sampler.event.ThemeEvent; import atlantafx.sampler.layout.MainModel.SubLayer; import atlantafx.sampler.page.Page; import atlantafx.sampler.theme.ThemeManager; import java.io.IOException; +import java.nio.file.Paths; import java.util.Objects; import javafx.animation.FadeTransition; import javafx.application.Platform; +import javafx.embed.swing.SwingFXUtils; +import javafx.scene.SnapshotParameters; +import javafx.scene.image.WritableImage; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyCodeCombination; +import javafx.scene.input.KeyCombination; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Pane; import javafx.scene.layout.StackPane; +import javafx.stage.FileChooser; import javafx.util.Duration; +import javax.imageio.ImageIO; class MainLayer extends BorderPane { @@ -42,6 +52,38 @@ class MainLayer extends BorderPane { // keyboard navigation won't work without focus Platform.runLater(sidebar::begForFocus); + + var snapshotKeys = new KeyCodeCombination(KeyCode.W, KeyCombination.CONTROL_DOWN); + DefaultEventBus.getInstance().subscribe(HotkeyEvent.class, e -> { + if (Objects.equals(e.getKeys(), snapshotKeys)) { + final Page page = (Page) subLayerPane.getChildren().stream() + .filter(c -> c instanceof Page) + .findFirst() + .orElse(null); + if (page == null || page.getSnapshotTarget() == null) { + return; + } + + var fileChooser = new FileChooser(); + fileChooser.setInitialDirectory(Paths.get(System.getProperty("user.home")).toFile()); + fileChooser.setInitialFileName("snapshot.png"); + fileChooser.getExtensionFilters().addAll( + new FileChooser.ExtensionFilter("Image Files", "*.png") + ); + + var file = fileChooser.showSaveDialog(getScene().getWindow()); + if (file == null) { + return; + } + + try { + WritableImage img = page.getSnapshotTarget().snapshot(new SnapshotParameters(), null); + ImageIO.write(SwingFXUtils.fromFXImage(img, null), "png", file); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + }); } private void createView() { diff --git a/sampler/src/main/java/atlantafx/sampler/page/AbstractPage.java b/sampler/src/main/java/atlantafx/sampler/page/AbstractPage.java index 8876629..c29ec70 100755 --- a/sampler/src/main/java/atlantafx/sampler/page/AbstractPage.java +++ b/sampler/src/main/java/atlantafx/sampler/page/AbstractPage.java @@ -20,6 +20,7 @@ import org.jetbrains.annotations.Nullable; public abstract class AbstractPage extends StackPane implements Page { protected final VBox userContent = new VBox(); + protected final StackPane userContentArea = new StackPane(userContent); protected Overlay overlay; protected boolean isRendered = false; @@ -33,7 +34,6 @@ public abstract class AbstractPage extends StackPane implements Page { } protected void createPageLayout() { - var userContentArea = new StackPane(userContent); userContentArea.setAlignment(Pos.TOP_CENTER); userContent.setMinWidth(Math.min(Page.MAX_WIDTH, 800)); userContent.setMaxWidth(Math.min(Page.MAX_WIDTH, 800)); @@ -65,6 +65,11 @@ public abstract class AbstractPage extends StackPane implements Page { return URI.create(String.format(JFX_JAVADOC_URI_TEMPLATE, "control/" + getName())); } + @Override + public Node getSnapshotTarget() { + return userContentArea; + } + @Override public void reset() { } diff --git a/sampler/src/main/java/atlantafx/sampler/page/OutlinePage.java b/sampler/src/main/java/atlantafx/sampler/page/OutlinePage.java index 3804372..2ad598a 100755 --- a/sampler/src/main/java/atlantafx/sampler/page/OutlinePage.java +++ b/sampler/src/main/java/atlantafx/sampler/page/OutlinePage.java @@ -37,6 +37,7 @@ public abstract class OutlinePage extends StackPane implements Page { protected final ScrollPane scrollPane = new ScrollPane(); protected final VBox userContent = new VBox(); + protected final StackPane userContentArea = new StackPane(userContent); protected final Outline outline = new Outline(createOutlineHandler()); protected Overlay overlay; @@ -52,7 +53,6 @@ public abstract class OutlinePage extends StackPane implements Page { } protected void createPageLayout() { - var userContentArea = new StackPane(userContent); StackPane.setMargin(userContent, new Insets(0, OUTLINE_WIDTH, 0, 0)); userContent.setMinWidth(Page.MAX_WIDTH - OUTLINE_WIDTH - 100); userContent.setMaxWidth(Page.MAX_WIDTH - OUTLINE_WIDTH - 100); @@ -177,6 +177,11 @@ public abstract class OutlinePage extends StackPane implements Page { return URI.create(String.format(JFX_JAVADOC_URI_TEMPLATE, "control/" + getName())); } + @Override + public Node getSnapshotTarget() { + return userContentArea; + } + @Override public void reset() { } diff --git a/sampler/src/main/java/atlantafx/sampler/page/Page.java b/sampler/src/main/java/atlantafx/sampler/page/Page.java index fa96d4d..7d0b019 100755 --- a/sampler/src/main/java/atlantafx/sampler/page/Page.java +++ b/sampler/src/main/java/atlantafx/sampler/page/Page.java @@ -27,6 +27,7 @@ import javafx.scene.control.MenuItem; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCodeCombination; import javafx.scene.layout.HBox; +import javafx.scene.layout.Pane; import net.datafaker.Faker; import org.jetbrains.annotations.Nullable; import org.kordamp.ikonli.feather.Feather; @@ -58,6 +59,8 @@ public interface Page { @Nullable URI getJavadocUri(); + @Nullable Node getSnapshotTarget(); + void reset(); default List generate(Supplier supplier, int count) { diff --git a/sampler/src/main/java/atlantafx/sampler/page/showcase/OverviewPage.java b/sampler/src/main/java/atlantafx/sampler/page/showcase/OverviewPage.java index 720f10d..892379c 100755 --- a/sampler/src/main/java/atlantafx/sampler/page/showcase/OverviewPage.java +++ b/sampler/src/main/java/atlantafx/sampler/page/showcase/OverviewPage.java @@ -11,6 +11,7 @@ import java.io.IOException; import java.net.URI; import javafx.fxml.FXMLLoader; import javafx.geometry.Pos; +import javafx.scene.Node; import javafx.scene.Parent; import javafx.scene.control.ScrollPane; import javafx.scene.layout.Pane; @@ -21,6 +22,7 @@ import org.jetbrains.annotations.Nullable; public final class OverviewPage extends ScrollPane implements Page { public static final String NAME = "Overview"; + private VBox wrapper; @Override public String getName() { @@ -31,7 +33,7 @@ public final class OverviewPage extends ScrollPane implements Page { super(); try { - var wrapper = new VBox(); + wrapper = new VBox(); wrapper.setAlignment(Pos.TOP_CENTER); var loader = new FXMLLoader( @@ -72,6 +74,11 @@ public final class OverviewPage extends ScrollPane implements Page { return null; } + @Override + public Node getSnapshotTarget() { + return wrapper; + } + @Override public void reset() { } diff --git a/sampler/src/main/java/atlantafx/sampler/page/showcase/ShowcasePage.java b/sampler/src/main/java/atlantafx/sampler/page/showcase/ShowcasePage.java index 25c870f..2dbecaf 100644 --- a/sampler/src/main/java/atlantafx/sampler/page/showcase/ShowcasePage.java +++ b/sampler/src/main/java/atlantafx/sampler/page/showcase/ShowcasePage.java @@ -111,6 +111,11 @@ public abstract class ShowcasePage extends StackPane implements Page { return null; } + @Override + public Node getSnapshotTarget() { + return showCaseContent; + } + @Override public void reset() { }