/* * 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 */ package com.anchorage.docks.containers.zones; import com.anchorage.docks.containers.common.AnchorageSettings; import static com.anchorage.docks.containers.common.AnchorageSettings.FLOATING_NODE_DROPSHADOW_RADIUS; import com.anchorage.docks.node.DockNode; import com.anchorage.docks.node.ui.DockUIPanel; import com.anchorage.docks.stations.DockStation; import com.anchorage.docks.stations.DockSubStation; import java.util.ArrayList; import java.util.List; import javafx.animation.Interpolator; import javafx.animation.KeyFrame; import javafx.animation.KeyValue; import javafx.animation.Timeline; import javafx.geometry.BoundingBox; import javafx.geometry.Bounds; import javafx.geometry.Point2D; import javafx.scene.Scene; import javafx.scene.image.Image; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; import javafx.scene.shape.Circle; import javafx.scene.shape.Rectangle; import javafx.stage.Stage; import javafx.stage.StageStyle; import javafx.util.Duration; /** * * @author avinerbi */ public final class DockZones extends Stage { private static final double CIRCLE_RADIUS = 100; public static final int OFFSET_IMAGE = 15; ///////// MAIN STAGE SCENE AND ROOT private Scene scene; private Pane mainRoot; //////////////////////////////////// /////////// CIRCLE EXTERNAL STAGE private Stage circleStage; private Scene circleStageScene; private Pane circleStageRoot; private Circle circleZone; //////////////////////////////////// private static Image dragTopImage; private static Image dragBottomImage; private static Image dragLeftImage; private static Image dragRightImage; private static Image dragCenterImage; private List<ZoneSelector> selectors; /////////////////////////////////////////////// private DockStation ownerStation; private Rectangle currentAreaSelected; private DockNode currentNodeTarget; private DockNode nodeToMove; private DockNode.DockPosition currentPosition; private ZoneSelector currentZoneSelector = null; private Rectangle rectanglePreview; private Timeline opacityAnimationPreview; static { dragTopImage = new Image("dragtop.png"); dragBottomImage = new Image("dragbottom.png"); dragLeftImage = new Image("dragleft.png"); dragRightImage = new Image("dragright.png"); dragCenterImage = new Image("dragcenter.png"); } private DockZones() { } public DockZones(DockStation station, DockNode nodeToMove) { this.nodeToMove = nodeToMove; this.ownerStation = station; initOwner(ownerStation.getStationWindow()); initStyle(StageStyle.TRANSPARENT); buildUI(); buildCircleStage(); makeSelectors(); createRectangleForPreview(); setAlwaysOnTop(true); circleStage.setAlwaysOnTop(true); } private void createRectangleForPreview() { rectanglePreview = new Rectangle(0, 0, 50, 50); rectanglePreview.getStyleClass().add("dockzone-rectangle-preview"); rectanglePreview.setOpacity(0); opacityAnimationPreview = new Timeline(new KeyFrame(Duration.seconds(0.5), new KeyValue(rectanglePreview.opacityProperty(), 0.5, Interpolator.LINEAR))); opacityAnimationPreview.setAutoReverse(true); opacityAnimationPreview.setCycleCount(-1); mainRoot.getChildren().add(rectanglePreview); } private void makeSelectors() { selectors = new ArrayList<>(); if (ownerStation.getChildren().size() > 0) { // selectors of station selectors.add(new ZoneSelector(dragTopImage, DockNode.DockPosition.TOP, true, mainRoot, (mainRoot.getWidth() - dragTopImage.getWidth()) / 2, OFFSET_IMAGE)); selectors.add(new ZoneSelector(dragBottomImage, DockNode.DockPosition.BOTTOM, true, mainRoot, (mainRoot.getWidth() - dragTopImage.getWidth()) / 2, mainRoot.getHeight() - dragBottomImage.getHeight() - OFFSET_IMAGE)); selectors.add(new ZoneSelector(dragLeftImage, DockNode.DockPosition.LEFT, true, mainRoot, OFFSET_IMAGE, (mainRoot.getHeight() - dragLeftImage.getWidth()) / 2)); selectors.add(new ZoneSelector(dragRightImage, DockNode.DockPosition.RIGHT, true, mainRoot, (mainRoot.getWidth() - dragRightImage.getWidth() - OFFSET_IMAGE), (mainRoot.getHeight() - dragRightImage.getWidth()) / 2)); // selectors of node selectors.add(new ZoneSelector(dragTopImage, DockNode.DockPosition.TOP, false, circleStageRoot, (circleStageRoot.getWidth() - dragTopImage.getWidth()) / 2, OFFSET_IMAGE)); selectors.add(new ZoneSelector(dragBottomImage, DockNode.DockPosition.BOTTOM, false, circleStageRoot, (circleStageRoot.getWidth() - dragBottomImage.getWidth()) / 2, circleStageRoot.getHeight() - dragBottomImage.getHeight() - OFFSET_IMAGE)); selectors.add(new ZoneSelector(dragLeftImage, DockNode.DockPosition.LEFT, false, circleStageRoot, OFFSET_IMAGE, (circleStageRoot.getHeight() - dragLeftImage.getHeight()) / 2)); selectors.add(new ZoneSelector(dragRightImage, DockNode.DockPosition.RIGHT, false, circleStageRoot, circleStageRoot.getWidth() - dragRightImage.getWidth() - OFFSET_IMAGE, (circleStageRoot.getHeight() - dragRightImage.getHeight()) / 2)); selectors.add(new ZoneSelector(dragCenterImage, DockNode.DockPosition.CENTER, false, circleStageRoot, (circleStageRoot.getWidth() - dragCenterImage.getWidth()) / 2, (circleStageRoot.getHeight() - dragCenterImage.getHeight()) / 2)); } else { selectors.add(new ZoneSelector(dragCenterImage, DockNode.DockPosition.CENTER, true, mainRoot, (mainRoot.getWidth() - dragCenterImage.getWidth()) / 2, (mainRoot.getHeight() - dragCenterImage.getHeight()) / 2)); } } private void buildCircleStage() { circleStage = new Stage(); circleStage.initStyle(StageStyle.TRANSPARENT); circleStage.initOwner(this); circleZone = new Circle(CIRCLE_RADIUS); circleZone.setCenterX(CIRCLE_RADIUS); circleZone.setCenterY(CIRCLE_RADIUS); circleZone.getStyleClass().add("dockzone-circle-container-selectors"); circleStageRoot = new Pane(circleZone); circleStageRoot.setStyle("-fx-background-color:rgba(0,0,0,0);"); circleStageScene = new Scene(circleStageRoot, CIRCLE_RADIUS * 2, CIRCLE_RADIUS * 2, Color.TRANSPARENT); circleStage.setScene(circleStageScene); circleStageRoot.setOpacity(0); } private void buildUI() { mainRoot = new Pane(); mainRoot.setStyle("-fx-background-color:rgba(0,0,0,0);"); scene = new Scene(mainRoot, ownerStation.getWidth(), ownerStation.getHeight(), Color.TRANSPARENT); setScene(scene); Point2D screenOrigin = ownerStation.localToScreen(ownerStation.getBoundsInLocal().getMinX(), ownerStation.getBoundsInLocal().getMinY()); setX(screenOrigin.getX()); setY(screenOrigin.getY()); } public void showZones() { show(); circleStage.show(); } public void moveAt(DockNode node) { currentNodeTarget = node; Bounds screenBounds = node.getScreenBounds(); if (circleStageRoot.opacityProperty().get() == 0) { circleStageRoot.setOpacity(1); } circleStage.setX(screenBounds.getMinX() + (screenBounds.getWidth() - circleStageRoot.getWidth()) / 2); circleStage.setY(screenBounds.getMinY() + (screenBounds.getHeight() - circleStageRoot.getHeight()) / 2); } public void hideCircleZones() { hidePreview(); currentNodeTarget = null; if (currentZoneSelector != null) { currentZoneSelector.reset(); } currentZoneSelector = null; currentPosition = null; circleStageRoot.setOpacity(0); } private void checkVisibilityConditions() { selectors. stream(). forEach(z -> z.setZoneDisabled(false)); if (currentNodeTarget == nodeToMove) { // disable border zones selectors. stream(). filter(z -> !z.isStationZone() && z.getPosition() != DockNode.DockPosition.CENTER). forEach(z -> z.setZoneDisabled(true)); } } public boolean searchArea(double x, double y) { checkVisibilityConditions(); ZoneSelector selector = selectors.stream() .filter(s -> s.overMe(x, y) && !s.isZoneDisabled()) .findFirst() .orElse(null); highLight(selector); if (selector != null && selector != currentZoneSelector) { currentZoneSelector = selector; makePreview(currentZoneSelector, currentNodeTarget); currentPosition = currentZoneSelector.getPosition(); } else { if (selector == null) { hidePreview(); currentZoneSelector = null; currentPosition = null; } } return selector != null; } public DockNode getCurrentNodeTarget() { return currentNodeTarget; } public ZoneSelector getCurrentZoneSelector() { return currentZoneSelector; } public DockNode getNodeSource() { return nodeToMove; } public DockNode.DockPosition getCurrentPosition() { return currentPosition; } private void highLight(ZoneSelector selector) { selectors.stream().forEach(s -> s.reset()); if (selector != null) { if (selector.isStationZone()) { circleStageRoot.setOpacity(0); } else { circleStageRoot.setOpacity(1); } selector.highLight(); } else { if (rectanglePreview != null) { hidePreview(); } currentNodeTarget = null; } } private DockStation getStation() { return ownerStation; } private void hidePreview() { if (AnchorageSettings.isDockingPositionPreview()) { stopAnimatePreview(); rectanglePreview.setOpacity(0); } } private void showPreview(Bounds sceneBounds, ZoneSelector selector) { if (selector.getPosition() == DockNode.DockPosition.LEFT) { rectanglePreview.setX(sceneBounds.getMinX()); rectanglePreview.setY(sceneBounds.getMinY()); rectanglePreview.setWidth(sceneBounds.getWidth() / 2); rectanglePreview.setHeight(sceneBounds.getHeight()); } if (selector.getPosition() == DockNode.DockPosition.TOP) { rectanglePreview.setX(sceneBounds.getMinX()); rectanglePreview.setY(sceneBounds.getMinY()); rectanglePreview.setWidth(sceneBounds.getWidth()); rectanglePreview.setHeight(sceneBounds.getHeight() / 2); } if (selector.getPosition() == DockNode.DockPosition.RIGHT) { rectanglePreview.setX(sceneBounds.getMinX() + sceneBounds.getWidth() / 2); rectanglePreview.setY(sceneBounds.getMinY()); rectanglePreview.setWidth(sceneBounds.getWidth() / 2); rectanglePreview.setHeight(sceneBounds.getHeight()); } if (selector.getPosition() == DockNode.DockPosition.BOTTOM) { rectanglePreview.setX(sceneBounds.getMinX()); rectanglePreview.setY(sceneBounds.getMinY() + sceneBounds.getHeight() / 2); rectanglePreview.setWidth(sceneBounds.getWidth()); rectanglePreview.setHeight(sceneBounds.getHeight() / 2); } if (selector.getPosition() == DockNode.DockPosition.CENTER) { rectanglePreview.setX(sceneBounds.getMinX()); rectanglePreview.setY(sceneBounds.getMinY()); rectanglePreview.setWidth(sceneBounds.getWidth()); rectanglePreview.setHeight(sceneBounds.getHeight()); } } private void animatePreview() { stopAnimatePreview(); rectanglePreview.setOpacity(1); rectanglePreview.toFront(); opacityAnimationPreview.play(); } private void stopAnimatePreview() { opacityAnimationPreview.stop(); } private void makePreview(ZoneSelector selector, DockNode currentNodeTarget) { if (AnchorageSettings.isDockingPositionPreview()) { if (selector.isStationZone()) { Bounds sceneBounds = ownerStation.getBoundsInParent(); showPreview(sceneBounds,selector); } else { Bounds nodeSceneBounds = currentNodeTarget.localToScene(currentNodeTarget.getBoundsInLocal()); Bounds stationSceneBounds = ownerStation.localToScene(ownerStation.getBoundsInLocal()); Bounds sceneBounds = new BoundingBox(nodeSceneBounds.getMinX()-stationSceneBounds.getMinX(), nodeSceneBounds.getMinY()-stationSceneBounds.getMinY(), nodeSceneBounds.getWidth(),nodeSceneBounds.getHeight()); if (ownerStation.isSubStation()) { DockSubStation subStationNode = ownerStation.getDockNodeForSubStation(); if (subStationNode.floatingProperty().get()) { sceneBounds = new BoundingBox(sceneBounds.getMinX()-FLOATING_NODE_DROPSHADOW_RADIUS-subStationNode.getFloatableStage().getPaddingOffset().getLeft(), sceneBounds.getMinY()-FLOATING_NODE_DROPSHADOW_RADIUS-subStationNode.getFloatableStage().getPaddingOffset().getTop()-DockUIPanel.BAR_HEIGHT, sceneBounds.getWidth(), sceneBounds.getHeight()); } } showPreview(sceneBounds,selector); } animatePreview(); } } }