Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/main/java/org/dockfx/DockNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -645,7 +645,7 @@ public String getName() {
};

public final boolean isTabbed() {
return floatingProperty.get();
return tabbedProperty.get();
}


Expand Down
22 changes: 16 additions & 6 deletions src/main/java/org/dockfx/DockPane.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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();

Expand All @@ -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);
}

/**
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/org/dockfx/DockTitleBar.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,
Expand Down Expand Up @@ -85,6 +87,7 @@ public void handle(ActionEvent event) {
dockNode.setMaximized(!dockNode.isMaximized());
} else {
dockNode.setFloating(true);
Platform.runLater(() -> DockPaneManager.createUndockedWindow(dockNode));
}
}
});
Expand Down Expand Up @@ -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);
}
}
}
}
87 changes: 24 additions & 63 deletions src/main/java/org/dockfx/demo/DockFX.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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<String> tableView = new TableView<String>();
// 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<String, String>("A"),
new TableColumn<String, String>("B"), new TableColumn<String, String>("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
Expand Down Expand Up @@ -144,4 +88,21 @@ private TreeView<String> 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;
}
}
6 changes: 4 additions & 2 deletions src/main/java/org/dockfx/pane/ContentTabPane.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;

/**
Expand All @@ -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() {
Expand Down Expand Up @@ -89,6 +89,8 @@ public List<Node> 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);
}
}
7 changes: 1 addition & 6 deletions src/main/java/org/dockfx/pane/DockNodeTab.java
Original file line number Diff line number Diff line change
@@ -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;

/**
Expand All @@ -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() {
Expand Down
69 changes: 69 additions & 0 deletions src/main/java/org/dockfx/pane/DockPaneManager.java
Original file line number Diff line number Diff line change
@@ -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<DockPane> 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);
}
}
});
});
}
}