/* * Copyright 2015-2016 Alessio Vinerbi. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301 USA */ /* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package com.anchorage.docks.node; import com.anchorage.docks.containers.StageFloatable; import static com.anchorage.docks.containers.common.AnchorageSettings.FLOATING_NODE_DROPSHADOW_RADIUS; import com.anchorage.docks.node.ui.DockUIPanel; import javafx.scene.layout.StackPane; import com.anchorage.docks.containers.interfaces.DockContainableComponent; import com.anchorage.docks.containers.interfaces.DockContainer; import com.anchorage.docks.containers.subcontainers.DockTabberContainer; import com.anchorage.docks.node.interfaces.DockNodeCloseRequestHandler; import com.anchorage.docks.node.interfaces.DockNodeCreationListener; import com.anchorage.docks.stations.DockStation; import com.anchorage.docks.stations.DockSubStation; import com.anchorage.system.AnchorageSystem; import java.util.Objects; import javafx.beans.property.BooleanProperty; import javafx.beans.property.ObjectProperty; import javafx.beans.property.ReadOnlyBooleanProperty; import javafx.beans.property.ReadOnlyBooleanWrapper; import javafx.beans.property.ReadOnlyObjectProperty; import javafx.beans.property.ReadOnlyObjectWrapper; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.collections.ObservableList; import javafx.geometry.Bounds; import javafx.geometry.Point2D; import javafx.geometry.Rectangle2D; import javafx.scene.image.Image; import javafx.scene.layout.Pane; import javafx.stage.Screen; import javafx.stage.Window; /** * * @author Alessio */ public class DockNode extends StackPane implements DockContainableComponent { public enum DockPosition { LEFT, RIGHT, TOP, BOTTOM, CENTER } private DockUIPanel content; private BooleanProperty floatableProperty; private BooleanProperty closeableProperty; private BooleanProperty resizableProperty; private BooleanProperty maximizableProperty; private ObjectProperty<DockStation> station; private ReadOnlyBooleanWrapper floatingProperty; private ReadOnlyBooleanWrapper draggingProperty; private ReadOnlyBooleanWrapper resizingProperty; private ReadOnlyBooleanWrapper maximizingProperty; private ReadOnlyObjectWrapper<DockContainer> container; private StageFloatable stageFloatable; private double floatingStateCoordinateX; private double floatingStateCoordinateY; private double floatingStateWidth; private double floatingStateHeight; private DockNodeCloseRequestHandler closeRequestHanlder; private DockNode() { station = new SimpleObjectProperty<>(null); floatableProperty = new SimpleBooleanProperty(true); closeableProperty = new SimpleBooleanProperty(true); resizableProperty = new SimpleBooleanProperty(true); maximizableProperty = new SimpleBooleanProperty(true); floatingProperty = new ReadOnlyBooleanWrapper(false); draggingProperty = new ReadOnlyBooleanWrapper(false); resizingProperty = new ReadOnlyBooleanWrapper(false); maximizingProperty = new ReadOnlyBooleanWrapper(false); container = new ReadOnlyObjectWrapper<>(null); } public DockNode(DockUIPanel node) { this(); this.content = node; buildUI(node); callCreationCallBack(); } private void callCreationCallBack() { if (content.getNodeContent() instanceof DockNodeCreationListener) { ((DockNodeCreationListener) content.getNodeContent()).onDockNodeCreated(this); } } public void ensureVisibility() { if (container.get() instanceof DockTabberContainer) { ((DockTabberContainer) container.get()).ensureVisibility(this); } } public void setCloseRequestHandler(DockNodeCloseRequestHandler handler) { Objects.requireNonNull(handler); closeRequestHanlder = handler; } public DockNodeCloseRequestHandler getCloseRequestHandler() { return closeRequestHanlder; } public BooleanProperty floatableProperty() { return floatableProperty; } public BooleanProperty closeableProperty() { return closeableProperty; } public BooleanProperty resizableProperty() { return resizableProperty; } public BooleanProperty maximizableProperty() { return maximizableProperty; } public ReadOnlyBooleanProperty floatingProperty() { return floatingProperty.getReadOnlyProperty(); } public ReadOnlyBooleanProperty draggingProperty() { return draggingProperty.getReadOnlyProperty(); } public ReadOnlyBooleanProperty maximizingProperty() { return maximizingProperty.getReadOnlyProperty(); } public ReadOnlyObjectProperty<DockContainer> containerProperty() { return container.getReadOnlyProperty(); } public void setIcon(Image icon) { content.setIcon(icon); } public void restore() { if (draggingProperty.get()) { closeFloatingStage(); } disableDragging(); } public boolean isDockVisible() { if (containerProperty().get() == null || floatingProperty.get()) { return true; } else { return container.get().isDockVisible(this); } } public void closeFloatingStage() { if (stageFloatable != null) { stageFloatable.closeStage(); stageFloatable = null; } floatingProperty.set(false); } public StageFloatable getFloatableStage() { return stageFloatable; } private void buildUI(DockUIPanel panel) { getChildren().add(panel); panel.setDockNode(this); } public void moveFloatable(double x, double y) { if (!maximizingProperty.get()) { stageFloatable.move(x, y); } } public void makeGhostFloatable(Window owner, double x, double y) { if (!floatingProperty.get()) { stageFloatable = new StageFloatable(this, owner, x, y); stageFloatable.show(); makeTransparent(); } } public void enableDraggingOnPosition(double x, double y) { draggingProperty.set(true); makeGhostFloatable(station.get().getScene().getWindow(), x, y); if (!maximizingProperty().get()) { AnchorageSystem.prepareDraggingZoneFor(station.get(), this); } } public void disableDragging() { draggingProperty.set(false); makeOpaque(); } private void makeTransparent() { content.setOpacity(0.4); } private void makeOpaque() { content.setOpacity(1); } public void makeNodeActiveOnFloatableStage(Window owner, double x, double y) { disableDragging(); if (!floatingProperty.get()) { if (floatableProperty.get()) { if (stageFloatable == null) { makeGhostFloatable(owner, x, y); } stageFloatable.makeNodeActiveOnFloatableStage(); floatingProperty.set(true); } else { closeFloatingStage(); } } } /** * Get the value of station * * @return the value of station */ public ObjectProperty<DockStation> stationProperty() { return station; } @Override public void setParentContainer(DockContainer container) { this.container.set(container); } @Override public DockContainer getParentContainer() { return container.get(); } public void dockAsFloating(Window owner, DockStation station, double x, double y, double width, double height) { if (stationProperty().get() != null) { ensureVisibility(); return; } station.add(this); stageFloatable = new StageFloatable(this, owner, x, y); stageFloatable.show(); stageFloatable.setWidth(width); stageFloatable.setHeight(height); floatingProperty.set(true); this.station.set((DockStation) station); } public void dock(DockStation station, DockPosition position) { if (stationProperty().get() != null) { ensureVisibility(); return; } station.add(this); station.putDock(this, position, 0.5); this.station.set((DockStation) station); } public void dock(DockNode nodeTarget, DockPosition position) { if (stationProperty().get() != null) { ensureVisibility(); return; } nodeTarget.stationProperty().get().add(this); nodeTarget.getParentContainer().putDock(this, nodeTarget, position, 0.5); station.set(nodeTarget.station.get()); } public void dock(DockStation station, DockPosition position, double percentage) { if (stationProperty().get() != null) { ensureVisibility(); return; } station.add(this); station.putDock(this, position, percentage); this.station.set((DockStation) station); } public void dock(DockNode nodeTarget, DockPosition position, double percentage) { if (stationProperty().get() != null) { ensureVisibility(); return; } nodeTarget.stationProperty().get().add(this); nodeTarget.getParentContainer().putDock(this, nodeTarget, position, percentage); station.set(nodeTarget.station.get()); } public void dock(DockSubStation subStation, DockPosition position) { if (stationProperty().get() != null) { ensureVisibility(); return; } subStation.putDock(this, position, 0.5); } public void dock(DockSubStation subStation, DockPosition position, double percentage) { if (stationProperty().get() != null) { ensureVisibility(); return; } subStation.putDock(this, position, percentage); } public void undock() { if (stationProperty().get() == null) { return; } boolean isFloating = floatingProperty.get(); restore(); if (getParentContainer() != null) { getParentContainer().undock(this); station.get().remove(this); } else if (isFloating) { station.get().remove(this); station.set(null); } } public DockUIPanel getContent() { return content; } @Override public String toString() { return content.titleProperty().get(); } public Bounds getSceneBounds() { return localToScene(getBoundsInLocal()); } public Bounds getScreenBounds() { return localToScreen(getBoundsInLocal()); } public boolean checkForTarget(double x, double y) { Point2D screenToScenePoint = getScene().getRoot().screenToLocal(x, y); Bounds sceneBounds = getSceneBounds(); return sceneBounds.contains(screenToScenePoint.getX(), screenToScenePoint.getY()); } public boolean insideTabContainer() { return container.get() instanceof DockTabberContainer; } public void maximizeOrRestore() { if (maximizingProperty.get()) { restoreLayout(); } else { maximizeLayout(); } } public void restoreLayout() { if (maximizableProperty.get()) { if (floatingProperty.get()) { stageFloatable.setX(floatingStateCoordinateX); stageFloatable.setY(floatingStateCoordinateY); stageFloatable.setWidth(floatingStateWidth); stageFloatable.setHeight(floatingStateHeight); maximizingProperty.set(false); } else if (station.get().restore(this)) { maximizingProperty.set(false); } } } private void moveStateToFullScreen() { // Get current screen of the stage ObservableList<Screen> screens = Screen.getScreensForRectangle(new Rectangle2D(stageFloatable.getX(), stageFloatable.getY(), stageFloatable.getWidth(), stageFloatable.getHeight())); // Change stage properties Rectangle2D bounds = screens.get(0).getBounds(); stageFloatable.setX(bounds.getMinX() - FLOATING_NODE_DROPSHADOW_RADIUS); stageFloatable.setY(bounds.getMinY() - FLOATING_NODE_DROPSHADOW_RADIUS); stageFloatable.setWidth(bounds.getWidth() + FLOATING_NODE_DROPSHADOW_RADIUS * 2); stageFloatable.setHeight(bounds.getHeight() + FLOATING_NODE_DROPSHADOW_RADIUS * 2); } public void maximizeLayout() { if (maximizableProperty.get()) { if (floatingProperty.get()) { floatingStateCoordinateX = stageFloatable.getX(); floatingStateCoordinateY = stageFloatable.getY(); floatingStateWidth = stageFloatable.getWidth(); floatingStateHeight = stageFloatable.getHeight(); moveStateToFullScreen(); maximizingProperty.set(true); } else if (station.get().maximize(this)) { maximizingProperty.set(true); } } } public boolean isMenuButtonEnable() { return content.isMenuButtonEnable(); } }