Support saving any page snapshot to a file

This commit is contained in:
mkpaz 2023-05-19 22:52:54 +04:00
parent bbec330216
commit 38ce8e9446
7 changed files with 72 additions and 4 deletions

@ -40,7 +40,8 @@ public class Launcher extends Application {
public static final List<KeyCodeCombination> SUPPORTED_HOTKEYS = List.of( public static final List<KeyCodeCombination> SUPPORTED_HOTKEYS = List.of(
new KeyCodeCombination(KeyCode.SLASH), 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) { public static void main(String[] args) {

@ -6,20 +6,30 @@ import static atlantafx.sampler.event.PageEvent.Action;
import static javafx.scene.layout.Priority.ALWAYS; import static javafx.scene.layout.Priority.ALWAYS;
import atlantafx.sampler.event.DefaultEventBus; import atlantafx.sampler.event.DefaultEventBus;
import atlantafx.sampler.event.HotkeyEvent;
import atlantafx.sampler.event.PageEvent; import atlantafx.sampler.event.PageEvent;
import atlantafx.sampler.event.ThemeEvent; import atlantafx.sampler.event.ThemeEvent;
import atlantafx.sampler.layout.MainModel.SubLayer; import atlantafx.sampler.layout.MainModel.SubLayer;
import atlantafx.sampler.page.Page; import atlantafx.sampler.page.Page;
import atlantafx.sampler.theme.ThemeManager; import atlantafx.sampler.theme.ThemeManager;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Paths;
import java.util.Objects; import java.util.Objects;
import javafx.animation.FadeTransition; import javafx.animation.FadeTransition;
import javafx.application.Platform; 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.BorderPane;
import javafx.scene.layout.HBox; import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane; import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import javafx.stage.FileChooser;
import javafx.util.Duration; import javafx.util.Duration;
import javax.imageio.ImageIO;
class MainLayer extends BorderPane { class MainLayer extends BorderPane {
@ -42,6 +52,38 @@ class MainLayer extends BorderPane {
// keyboard navigation won't work without focus // keyboard navigation won't work without focus
Platform.runLater(sidebar::begForFocus); 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() { private void createView() {

@ -20,6 +20,7 @@ import org.jetbrains.annotations.Nullable;
public abstract class AbstractPage extends StackPane implements Page { public abstract class AbstractPage extends StackPane implements Page {
protected final VBox userContent = new VBox(); protected final VBox userContent = new VBox();
protected final StackPane userContentArea = new StackPane(userContent);
protected Overlay overlay; protected Overlay overlay;
protected boolean isRendered = false; protected boolean isRendered = false;
@ -33,7 +34,6 @@ public abstract class AbstractPage extends StackPane implements Page {
} }
protected void createPageLayout() { protected void createPageLayout() {
var userContentArea = new StackPane(userContent);
userContentArea.setAlignment(Pos.TOP_CENTER); userContentArea.setAlignment(Pos.TOP_CENTER);
userContent.setMinWidth(Math.min(Page.MAX_WIDTH, 800)); userContent.setMinWidth(Math.min(Page.MAX_WIDTH, 800));
userContent.setMaxWidth(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())); return URI.create(String.format(JFX_JAVADOC_URI_TEMPLATE, "control/" + getName()));
} }
@Override
public Node getSnapshotTarget() {
return userContentArea;
}
@Override @Override
public void reset() { public void reset() {
} }

@ -37,6 +37,7 @@ public abstract class OutlinePage extends StackPane implements Page {
protected final ScrollPane scrollPane = new ScrollPane(); protected final ScrollPane scrollPane = new ScrollPane();
protected final VBox userContent = new VBox(); protected final VBox userContent = new VBox();
protected final StackPane userContentArea = new StackPane(userContent);
protected final Outline outline = new Outline(createOutlineHandler()); protected final Outline outline = new Outline(createOutlineHandler());
protected Overlay overlay; protected Overlay overlay;
@ -52,7 +53,6 @@ public abstract class OutlinePage extends StackPane implements Page {
} }
protected void createPageLayout() { protected void createPageLayout() {
var userContentArea = new StackPane(userContent);
StackPane.setMargin(userContent, new Insets(0, OUTLINE_WIDTH, 0, 0)); StackPane.setMargin(userContent, new Insets(0, OUTLINE_WIDTH, 0, 0));
userContent.setMinWidth(Page.MAX_WIDTH - OUTLINE_WIDTH - 100); userContent.setMinWidth(Page.MAX_WIDTH - OUTLINE_WIDTH - 100);
userContent.setMaxWidth(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())); return URI.create(String.format(JFX_JAVADOC_URI_TEMPLATE, "control/" + getName()));
} }
@Override
public Node getSnapshotTarget() {
return userContentArea;
}
@Override @Override
public void reset() { public void reset() {
} }

@ -27,6 +27,7 @@ import javafx.scene.control.MenuItem;
import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.KeyCodeCombination;
import javafx.scene.layout.HBox; import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import net.datafaker.Faker; import net.datafaker.Faker;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.kordamp.ikonli.feather.Feather; import org.kordamp.ikonli.feather.Feather;
@ -58,6 +59,8 @@ public interface Page {
@Nullable URI getJavadocUri(); @Nullable URI getJavadocUri();
@Nullable Node getSnapshotTarget();
void reset(); void reset();
default <T> List<T> generate(Supplier<T> supplier, int count) { default <T> List<T> generate(Supplier<T> supplier, int count) {

@ -11,6 +11,7 @@ import java.io.IOException;
import java.net.URI; import java.net.URI;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
import javafx.geometry.Pos; import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Parent; import javafx.scene.Parent;
import javafx.scene.control.ScrollPane; import javafx.scene.control.ScrollPane;
import javafx.scene.layout.Pane; import javafx.scene.layout.Pane;
@ -21,6 +22,7 @@ import org.jetbrains.annotations.Nullable;
public final class OverviewPage extends ScrollPane implements Page { public final class OverviewPage extends ScrollPane implements Page {
public static final String NAME = "Overview"; public static final String NAME = "Overview";
private VBox wrapper;
@Override @Override
public String getName() { public String getName() {
@ -31,7 +33,7 @@ public final class OverviewPage extends ScrollPane implements Page {
super(); super();
try { try {
var wrapper = new VBox(); wrapper = new VBox();
wrapper.setAlignment(Pos.TOP_CENTER); wrapper.setAlignment(Pos.TOP_CENTER);
var loader = new FXMLLoader( var loader = new FXMLLoader(
@ -72,6 +74,11 @@ public final class OverviewPage extends ScrollPane implements Page {
return null; return null;
} }
@Override
public Node getSnapshotTarget() {
return wrapper;
}
@Override @Override
public void reset() { public void reset() {
} }

@ -111,6 +111,11 @@ public abstract class ShowcasePage extends StackPane implements Page {
return null; return null;
} }
@Override
public Node getSnapshotTarget() {
return showCaseContent;
}
@Override @Override
public void reset() { public void reset() {
} }