From c5b7016c159c93dcede00d42026edbcf08fd0359 Mon Sep 17 00:00:00 2001 From: Alex Koon Date: Sun, 11 Dec 2016 17:22:51 +0000 Subject: [PATCH] Every window is now a tab on content pane for a consistent look in dock panes. An undocked pane creates its own window where further windows can be docked. Allows an app to have multiple windows across monitors for example. Empty windows cleaned up. Bug fix for isTabbed in DockNode - not uses the TabbedProperty value. --- src/main/java/org/dockfx/DockNode.java | 2 +- src/main/java/org/dockfx/DockPane.java | 22 +++-- src/main/java/org/dockfx/DockTitleBar.java | 8 ++ src/main/java/org/dockfx/demo/DockFX.java | 87 +++++-------------- .../java/org/dockfx/pane/ContentTabPane.java | 6 +- .../java/org/dockfx/pane/DockNodeTab.java | 7 +- .../java/org/dockfx/pane/DockPaneManager.java | 69 +++++++++++++++ 7 files changed, 123 insertions(+), 78 deletions(-) create mode 100644 src/main/java/org/dockfx/pane/DockPaneManager.java diff --git a/src/main/java/org/dockfx/DockNode.java b/src/main/java/org/dockfx/DockNode.java index 554ae7c..d01e02b 100644 --- a/src/main/java/org/dockfx/DockNode.java +++ b/src/main/java/org/dockfx/DockNode.java @@ -645,7 +645,7 @@ public String getName() { }; public final boolean isTabbed() { - return floatingProperty.get(); + return tabbedProperty.get(); } diff --git a/src/main/java/org/dockfx/DockPane.java b/src/main/java/org/dockfx/DockPane.java index 1ad38ed..c1c3f48 100644 --- a/src/main/java/org/dockfx/DockPane.java +++ b/src/main/java/org/dockfx/DockPane.java @@ -25,10 +25,8 @@ import com.sun.javafx.css.StyleManager; -import org.dockfx.pane.ContentPane; -import org.dockfx.pane.ContentSplitPane; -import org.dockfx.pane.ContentTabPane; -import org.dockfx.pane.DockNodeTab; +import javafx.application.Platform; +import org.dockfx.pane.*; import javafx.animation.KeyFrame; import javafx.animation.KeyValue; @@ -366,7 +364,10 @@ public void dock(Node node, DockPos dockPos, Node sibling) { ContentPane pane = (ContentPane) root; if (pane == null) { - pane = new ContentSplitPane(node); + ContentTabPane contentTabPane = new ContentTabPane(); + pane = new ContentSplitPane(contentTabPane); + contentTabPane.setContentParent(pane); + contentTabPane.addNode(null, null, node, dockPos); root = (Node) pane; this.getChildren().add(root); return; @@ -422,6 +423,11 @@ public void dock(Node node, DockPos dockPos, Node sibling) { pane = split; } + ContentTabPane contentTabPane = new ContentTabPane(); + contentTabPane.setContentParent(pane); + pane.addNode(root, sibling, contentTabPane, dockPos); + pane = contentTabPane; + } else if (pane instanceof ContentTabPane) { ContentSplitPane split = (ContentSplitPane) pane.getContentParent(); @@ -445,12 +451,16 @@ public void dock(Node node, DockPos dockPos, Node sibling) { } split.setOrientation(requestedOrientation); - pane = split; + ContentTabPane contentTabPane = new ContentTabPane(); + contentTabPane.setContentParent(split); + split.addNode(root, sibling, contentTabPane, dockPos); + pane = contentTabPane; } } // Add a node to the proper pane pane.addNode(root, sibling, node, dockPos); + Platform.runLater(DockPaneManager::checkEmptyPanes); } /** diff --git a/src/main/java/org/dockfx/DockTitleBar.java b/src/main/java/org/dockfx/DockTitleBar.java index 4b1a2df..b98a158 100644 --- a/src/main/java/org/dockfx/DockTitleBar.java +++ b/src/main/java/org/dockfx/DockTitleBar.java @@ -25,6 +25,7 @@ import com.sun.javafx.stage.StageHelper; +import javafx.application.Platform; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.ActionEvent; @@ -43,6 +44,7 @@ import javafx.scene.layout.Priority; import javafx.stage.Stage; import javafx.stage.Window; +import org.dockfx.pane.DockPaneManager; /** * Base class for a dock node title bar that provides the mouse dragging functionality, captioning, @@ -85,6 +87,7 @@ public void handle(ActionEvent event) { dockNode.setMaximized(!dockNode.isMaximized()); } else { dockNode.setFloating(true); + Platform.runLater(() -> DockPaneManager.createUndockedWindow(dockNode)); } } }); @@ -426,6 +429,11 @@ public void run(Node node, Node dragNode) { dockPane.removeEventFilter(MouseEvent.MOUSE_DRAGGED, this); dockPane.removeEventFilter(MouseEvent.MOUSE_RELEASED, this); } + + if (!dockNode.isDocked()) { + Platform.runLater(() -> DockPaneManager.createUndockedWindow(dockNode)); + Platform.runLater(DockPaneManager::checkEmptyPanes); + } } } } diff --git a/src/main/java/org/dockfx/demo/DockFX.java b/src/main/java/org/dockfx/demo/DockFX.java index 6810fff..129e0ca 100644 --- a/src/main/java/org/dockfx/demo/DockFX.java +++ b/src/main/java/org/dockfx/demo/DockFX.java @@ -21,30 +21,24 @@ package org.dockfx.demo; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; import java.util.Random; +import javafx.scene.control.*; +import javafx.scene.layout.BorderPane; import org.dockfx.DockNode; import org.dockfx.DockPane; import org.dockfx.DockPos; import javafx.application.Application; import javafx.scene.Scene; -import javafx.scene.control.Tab; -import javafx.scene.control.TabPane; -import javafx.scene.control.TableColumn; -import javafx.scene.control.TableView; -import javafx.scene.control.TreeItem; -import javafx.scene.control.TreeView; import javafx.scene.image.Image; import javafx.scene.image.ImageView; -import javafx.scene.web.HTMLEditor; import javafx.stage.Stage; public class DockFX extends Application { + private DockPane dockPane = new DockPane(); + public static void main(String[] args) { launch(args); } @@ -54,64 +48,14 @@ public static void main(String[] args) { public void start(Stage primaryStage) { primaryStage.setTitle("DockFX"); - // create a dock pane that will manage our dock nodes and handle the layout - DockPane dockPane = new DockPane(); - - // create a default test node for the center of the dock area - TabPane tabs = new TabPane(); - HTMLEditor htmlEditor = new HTMLEditor(); - try { - htmlEditor.setHtmlText(new String(Files.readAllBytes(Paths.get("readme.html")))); - } catch (IOException e) { - e.printStackTrace(); - } + BorderPane borderPane = new BorderPane(dockPane); + borderPane.setTop(createMenuBar()); - // empty tabs ensure that dock node has its own background color when floating - tabs.getTabs().addAll(new Tab("Tab 1", htmlEditor), new Tab("Tab 2"), new Tab("Tab 3")); - - TableView tableView = new TableView(); - // this is why @SupressWarnings is used above - // we don't care about the warnings because this is just a demonstration - // for docks not the table view - tableView.getColumns().addAll(new TableColumn("A"), - new TableColumn("B"), new TableColumn("C")); - - // load an image to caption the dock nodes - Image dockImage = new Image(DockFX.class.getResource("docknode.png").toExternalForm()); - - // create and dock some prototype dock nodes to the middle of the dock pane - // the preferred sizes are used to specify the relative size of the node - // to the other nodes - - // we can use this to give our central content a larger area where - // the top and bottom nodes have a preferred width of 300 which means that - // when a node is docked relative to them such as the left or right dock below - // they will have 300 / 100 + 300 (400) or 75% of their previous width - // after both the left and right node's are docked the center docks end up with 50% of the width - - DockNode tabsDock = new DockNode(tabs, "Tabs Dock", new ImageView(dockImage)); - tabsDock.setPrefSize(300, 100); - tabsDock.dock(dockPane, DockPos.TOP); - DockNode tableDock = new DockNode(tableView); - // let's disable our table from being undocked - tableDock.setDockTitleBar(null); - tableDock.setPrefSize(300, 100); - tableDock.dock(dockPane, DockPos.BOTTOM); - - primaryStage.setScene(new Scene(dockPane, 800, 500)); + primaryStage.setScene(new Scene(borderPane, 800, 500)); primaryStage.sizeToScene(); primaryStage.show(); - // can be created and docked before or after the scene is created - // and the stage is shown - DockNode treeDock = new DockNode(generateRandomTree(), "Tree Dock", new ImageView(dockImage)); - treeDock.setPrefSize(100, 100); - treeDock.dock(dockPane, DockPos.LEFT); - treeDock = new DockNode(generateRandomTree(), "Tree Dock", new ImageView(dockImage)); - treeDock.setPrefSize(100, 100); - treeDock.dock(dockPane, DockPos.RIGHT); - // test the look and feel with both Caspian and Modena Application.setUserAgentStylesheet(Application.STYLESHEET_MODENA); // initialize the default styles for the dock pane and undocked nodes using the DockFX @@ -144,4 +88,21 @@ private TreeView generateRandomTree() { return treeView; } + + private MenuBar createMenuBar() { + final MenuBar menuBar = new MenuBar(); + Menu menu = new Menu("Window"); + MenuItem mi = new MenuItem("New Window"); + mi.setOnAction(event -> { + // load an image to caption the dock nodes + Image dockImage = new Image(DockFX.class.getResource("docknode.png").toExternalForm()); + DockNode treeDock = new DockNode(generateRandomTree(), "Tree Dock", new ImageView(dockImage)); + treeDock.setPrefSize(100, 100); + treeDock.dock(dockPane, DockPos.LEFT); + + }); + menu.getItems().add(mi); + menuBar.getMenus().add(menu); + return menuBar; + } } diff --git a/src/main/java/org/dockfx/pane/ContentTabPane.java b/src/main/java/org/dockfx/pane/ContentTabPane.java index ec3c4b9..3e08c4e 100644 --- a/src/main/java/org/dockfx/pane/ContentTabPane.java +++ b/src/main/java/org/dockfx/pane/ContentTabPane.java @@ -10,7 +10,6 @@ import javafx.scene.Node; import javafx.scene.Parent; -import javafx.scene.control.Tab; import javafx.scene.control.TabPane; /** @@ -24,6 +23,7 @@ public class ContentTabPane extends TabPane implements ContentPane { public ContentTabPane() { this.setStyle("-fx-skin: \"org.dockfx.pane.skin.ContentTabPaneSkin\";"); + setPrefSize(100.0, 100.0); } public Type getType() { @@ -89,6 +89,8 @@ public List getChildrenList() { public void addNode(Node root, Node sibling, Node node, DockPos dockPos) { DockNode newNode = (DockNode) node; - getTabs().add(new DockNodeTab(newNode)); + final DockNodeTab tab = new DockNodeTab(newNode); + getTabs().add(tab); + getSelectionModel().select(tab); } } diff --git a/src/main/java/org/dockfx/pane/DockNodeTab.java b/src/main/java/org/dockfx/pane/DockNodeTab.java index 76d4ab6..e1daa1c 100644 --- a/src/main/java/org/dockfx/pane/DockNodeTab.java +++ b/src/main/java/org/dockfx/pane/DockNodeTab.java @@ -1,14 +1,8 @@ package org.dockfx.pane; -import javafx.beans.InvalidationListener; -import javafx.beans.binding.Bindings; import org.dockfx.DockNode; import javafx.beans.property.SimpleStringProperty; -import javafx.beans.property.StringProperty; -import javafx.beans.value.ChangeListener; -import javafx.beans.value.ObservableValue; -import javafx.scene.control.Label; import javafx.scene.control.Tab; /** @@ -32,6 +26,7 @@ public DockNodeTab(DockNode node) { setGraphic(dockNode.getDockTitleBar()); setContent(dockNode); dockNode.tabbedProperty().set(true); + dockNode.setPrefSize(100.0, 100.0); } public String getTitle() { diff --git a/src/main/java/org/dockfx/pane/DockPaneManager.java b/src/main/java/org/dockfx/pane/DockPaneManager.java new file mode 100644 index 0000000..9921993 --- /dev/null +++ b/src/main/java/org/dockfx/pane/DockPaneManager.java @@ -0,0 +1,69 @@ +package org.dockfx.pane; + +import java.util.HashSet; +import java.util.Set; + +import javafx.application.Platform; +import javafx.scene.Scene; +import javafx.scene.image.Image; +import javafx.scene.layout.BorderPane; +import javafx.stage.Stage; +import org.dockfx.DockNode; +import org.dockfx.DockPane; +import org.dockfx.DockPos; +import org.dockfx.demo.DockFX; + +/** + * Utility class to handle dock panes and undocked windows management. + */ +public class DockPaneManager { + // load an image to caption the dock nodes + private static final Image DOCK_IMG = new Image(DockFX.class.getResource("docknode.png").toExternalForm()); + + private static final Set DOCK_PANES = new HashSet<>(); + private static final DockPane ROOT_PANE = new DockPane(); + + public static DockPane rootPane() { + return ROOT_PANE; + } + + private static DockPane createDockPane() { + final DockPane dockPane = new DockPane(); + DOCK_PANES.add(dockPane); + return dockPane; + } + + public static void createUndockedWindow(final DockNode dockNode) { + undockedWindowImpl(dockNode).show(); + } + + private static Stage undockedWindowImpl(final DockNode dockNode) { + final DockPane dockPane = DockPaneManager.createDockPane(); + final BorderPane borderPane = new BorderPane(); + borderPane.setCenter(dockPane); + final Scene scene = new Scene(borderPane); + final Stage stage = new Stage(); + stage.setScene(scene); + + borderPane.setPrefSize(dockNode.getStage().getWidth(), dockNode.getStage().getHeight()); + dockNode.dock(dockPane, DockPos.LEFT); + stage.setX(dockNode.getStage().getX()); + stage.setY(dockNode.getStage().getY()); + stage.sizeToScene(); + return stage; + } + + public static void checkEmptyPanes() { + DOCK_PANES.forEach(dp -> { + dp.getChildren().forEach(node -> { + if (node instanceof ContentSplitPane) { + ContentSplitPane csp = (ContentSplitPane) node; + if (csp.getChildrenList().isEmpty()) { + final Stage stage = (Stage) dp.getScene().getWindow(); + Platform.runLater(stage::close); + } + } + }); + }); + } +} \ No newline at end of file