/* * 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.containers; import static com.anchorage.docks.containers.common.AnchorageSettings.FLOATING_NODE_DROPSHADOW_RADIUS; import static com.anchorage.docks.containers.common.AnchorageSettings.FLOATING_NODE_MINIMUM_HEIGHT; import static com.anchorage.docks.containers.common.AnchorageSettings.FLOATING_NODE_MINIMUM_WIDTH; import com.anchorage.docks.node.DockNode; import com.anchorage.docks.stations.DockStation; import javafx.event.EventHandler; import javafx.geometry.Insets; import javafx.geometry.Point2D; import javafx.scene.Cursor; import javafx.scene.Scene; import javafx.scene.SnapshotParameters; import javafx.scene.effect.BlurType; import javafx.scene.effect.DropShadow; import javafx.scene.image.ImageView; import javafx.scene.image.WritableImage; import javafx.scene.input.MouseEvent; import javafx.scene.layout.StackPane; import javafx.scene.paint.Color; import javafx.stage.Stage; import javafx.stage.StageStyle; import javafx.stage.Window; /** * * @author avinerbi */ public class StageFloatable extends Stage { private Scene scene; private DockNode node; private StackPane transparentRootPanel; private StackPane stackPanelContainer; private Window owner; private Point2D mousePositionStart; private EventHandler<MouseEvent> eventsHandler; private double startX; private double startWidth; private double startY; private double startHeight; private ImageView imageView; private WritableImage ghostImage; private StageFloatable() { } public StageFloatable(DockNode node, Window owner, double startX, double startY) { this.node = node; this.owner = owner; buildUI(startX, startY); setAlwaysOnTop(true); } private void setupMouseEvents() { eventsHandler = event -> { if (event.getEventType() == MouseEvent.MOUSE_PRESSED) { mousePositionStart = new Point2D(event.getScreenX(), event.getScreenY()); startWidth = getWidth(); startX = getX(); startHeight = getHeight(); startY = getY(); } if (event.getEventType() == MouseEvent.MOUSE_MOVED) { boolean sizeRight = valueInRange(event.getX(), stackPanelContainer.getWidth() - stackPanelContainer.getPadding().getLeft(), stackPanelContainer.getWidth()); boolean sizeLeft = valueInRange(event.getX(), 0, stackPanelContainer.getPadding().getRight()); boolean sizeTop = valueInRange(event.getY(), 0, stackPanelContainer.getPadding().getTop()); boolean sizeBottom = valueInRange(event.getY(), stackPanelContainer.getHeight() - stackPanelContainer.getPadding().getBottom(), stackPanelContainer.getHeight()); Cursor cursor = changeCursor(sizeLeft, sizeRight, sizeTop, sizeBottom); getScene().setCursor(cursor); } if (event.getEventType() == MouseEvent.MOUSE_DRAGGED && (getScene().getCursor() != null && getScene().getCursor() != Cursor.DEFAULT)) { if (getScene().getCursor() == Cursor.E_RESIZE || getScene().getCursor() == Cursor.SE_RESIZE || getScene().getCursor() == Cursor.NE_RESIZE) { if (event.getScreenX() - getX() + FLOATING_NODE_DROPSHADOW_RADIUS > FLOATING_NODE_MINIMUM_WIDTH) { setWidth(event.getScreenX() - getX() + FLOATING_NODE_DROPSHADOW_RADIUS); } } else if (getScene().getCursor() == Cursor.S_RESIZE || getScene().getCursor() == Cursor.SE_RESIZE || getScene().getCursor() == Cursor.SW_RESIZE) { if (event.getScreenY() - getY() + FLOATING_NODE_DROPSHADOW_RADIUS > FLOATING_NODE_MINIMUM_HEIGHT) { setHeight(event.getScreenY() - getY() + FLOATING_NODE_DROPSHADOW_RADIUS); } } else if (getScene().getCursor() == Cursor.W_RESIZE || getScene().getCursor() == Cursor.NW_RESIZE || getScene().getCursor() == Cursor.SW_RESIZE) { double newX = event.getScreenX() - FLOATING_NODE_DROPSHADOW_RADIUS; double newWidth = startX - newX + startWidth; if (newWidth > FLOATING_NODE_MINIMUM_WIDTH) { setX(newX); setWidth(newWidth); } } else if (getScene().getCursor() == Cursor.N_RESIZE || getScene().getCursor() == Cursor.NW_RESIZE || getScene().getCursor() == Cursor.NE_RESIZE) { double newY = event.getScreenY() - FLOATING_NODE_DROPSHADOW_RADIUS; double newHeight = startY - newY + startHeight; if (newHeight > FLOATING_NODE_MINIMUM_HEIGHT) { setY(newY); setHeight(newHeight); } } } /* else if (event.getEventType() == MouseEvent.MOUSE_RELEASED) { // TODO: handle this event? } */ }; stackPanelContainer.addEventFilter(MouseEvent.MOUSE_PRESSED, eventsHandler); stackPanelContainer.addEventFilter(MouseEvent.MOUSE_MOVED, eventsHandler); stackPanelContainer.addEventFilter(MouseEvent.MOUSE_DRAGGED, eventsHandler); stackPanelContainer.addEventFilter(MouseEvent.MOUSE_RELEASED, eventsHandler); } public boolean inResizing() { return (getScene().getCursor() != null && getScene().getCursor() != Cursor.DEFAULT); } private Cursor changeCursor(boolean sizeLeft, boolean sizeRight, boolean sizeTop, boolean sizeBottom) { Cursor cursor = Cursor.DEFAULT; if (sizeLeft) { if (sizeTop) { cursor = Cursor.NW_RESIZE; } else if (sizeBottom) { cursor = Cursor.SW_RESIZE; } else { cursor = Cursor.W_RESIZE; } } else if (sizeRight) { if (sizeTop) { cursor = Cursor.NE_RESIZE; } else if (sizeBottom) { cursor = Cursor.SE_RESIZE; } else { cursor = Cursor.E_RESIZE; } } else if (sizeTop) { cursor = Cursor.N_RESIZE; } else if (sizeBottom) { cursor = Cursor.S_RESIZE; } return cursor; } private boolean valueInRange(double value, double min, double max) { return (value >= min && value <= max); } private void removeMouseEvents() { removeEventFilter(MouseEvent.MOUSE_PRESSED, eventsHandler); removeEventFilter(MouseEvent.MOUSE_MOVED, eventsHandler); removeEventFilter(MouseEvent.MOUSE_DRAGGED, eventsHandler); removeEventFilter(MouseEvent.MOUSE_RELEASED, eventsHandler); } private void buildUI(double startX, double startY) { initOwner(owner); setX(startX - FLOATING_NODE_DROPSHADOW_RADIUS); setY(startY - FLOATING_NODE_DROPSHADOW_RADIUS); createContainerPanel(); initStyle(StageStyle.TRANSPARENT); scene = new Scene(transparentRootPanel, node.getWidth() + FLOATING_NODE_DROPSHADOW_RADIUS * 2, node.getHeight() + FLOATING_NODE_DROPSHADOW_RADIUS * 2, Color.TRANSPARENT); setOnShown(e -> { setWidth(getWidth() + stackPanelContainer.getPadding().getLeft() + stackPanelContainer.getPadding().getRight()); setHeight(getHeight() + stackPanelContainer.getPadding().getTop() + stackPanelContainer.getPadding().getBottom()); }); setScene(scene); } private void createContainerPanel() { ghostImage = node.snapshot(new SnapshotParameters(), null); imageView = new ImageView(ghostImage); stackPanelContainer = new StackPane(imageView); transparentRootPanel = new StackPane(stackPanelContainer); transparentRootPanel.setPadding(new Insets(FLOATING_NODE_DROPSHADOW_RADIUS)); transparentRootPanel.setStyle("-fx-background-color:rgba(0,0,0,0);"); stackPanelContainer.getStyleClass().add("docknode-floating-stack-container-panel"); stackPanelContainer.setEffect(new DropShadow(BlurType.GAUSSIAN, new Color(0, 0, 0, 0.6), FLOATING_NODE_DROPSHADOW_RADIUS, 0.2, 0, 0)); stackPanelContainer.relocate(FLOATING_NODE_DROPSHADOW_RADIUS, FLOATING_NODE_DROPSHADOW_RADIUS); } public void move(double x, double y) { setX(x); setY(y); } public void makeNodeActiveOnFloatableStage() { DockStation station = node.stationProperty().get(); // save the station node.undock(); node.stationProperty().set(station); // resume station stackPanelContainer.getChildren().remove(imageView); stackPanelContainer.getChildren().add(node); if (node.resizableProperty().get()) { setupMouseEvents(); } } public void closeStage() { transparentRootPanel.getChildren().removeAll(); setScene(null); hide(); } public void makeFloatable() { } public Insets getPaddingOffset() { return stackPanelContainer.getPadding(); } }