Skip to content
Snippets Groups Projects
Commit 3eb22c5c authored by Simon Barner's avatar Simon Barner
Browse files

Merge branch '4181' into 'master'

Improve usability of diagram viewer

See merge request !163
parents 0240b86e e26c20fe
Branches
Tags
1 merge request!163Improve usability of diagram viewer
DiagramCoordinate.java 6b00aec99054d4cd19003a72bd4e5e774ac6a641 GREEN DiagramCoordinate.java 6b00aec99054d4cd19003a72bd4e5e774ac6a641 GREEN
DiagramLayers.java 155cbb47a5f0aaa0025320ae607e6777f3a2d2e8 GREEN DiagramLayers.java aa1f95dbae290c8b00202abe4385b01b8f36e5ab GREEN
DiagramViewer.java 920bb0f4ee6dd9ac6b607e44c01f04a413b2e2ed GREEN DiagramViewer.java e5afa84170823a396c2f80864eb752366d34eb92 GREEN
DiagramViewerDefaultTags.java 6230763252409c60009ab8887b4ef582cf883229 GREEN DiagramViewerDefaultTags.java 6230763252409c60009ab8887b4ef582cf883229 GREEN
DiagramViewerFeatures.java 397c9600193df18e865f1ff7c829df577c56d383 GREEN DiagramViewerFeatures.java 3dd78d9c117fc156924a151c6f8d770c53c103bc GREEN
DiagramViewerSelection.java e833f592543bc97077907d980a39b123fc4044e6 GREEN DiagramViewerSelection.java e833f592543bc97077907d980a39b123fc4044e6 GREEN
EDragGesture.java 5cfa098d3877db11981c2750e5e103156d62fc5e GREEN EDragGesture.java 5cfa098d3877db11981c2750e5e103156d62fc5e GREEN
FeedbackChange.java b088fa89af648f1674f2f9c1f7f99d585ce801ca GREEN FeedbackChange.java b088fa89af648f1674f2f9c1f7f99d585ce801ca GREEN
GridCanvasVisual.java 734027d56af342cd01ff445ba9347b8dbb6c83c2 GREEN GridCanvasVisual.java e7c83211e0fce2b0d55aac77d0203950286646b9 GREEN
MVCBundleManager.java e4892a571fd26eccc5e4e9b2256432721723f542 GREEN MVCBundleManager.java 18667b4ed98da124b7c1bc7a103e95232df9ad49 GREEN
MouseState.java 3d9993f799d5d74bc74ac03b46e4a1857c4d267e GREEN MouseState.java 3d9993f799d5d74bc74ac03b46e4a1857c4d267e GREEN
SVGExporter.java cbbd1eceb2910fd5c1693e05c5303a193127b9db GREEN SVGExporter.java cbbd1eceb2910fd5c1693e05c5303a193127b9db GREEN
...@@ -14,12 +14,9 @@ import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundle; ...@@ -14,12 +14,9 @@ import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundle;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.scene.Group; import javafx.scene.Group;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.transform.Scale;
/** {@link Group} of layers for the {@link DiagramViewer}. */ /** {@link Group} of layers for the {@link DiagramViewer}. */
public final class DiagramLayers extends Group { public final class DiagramLayers extends Group {
/** The diagram viewer of this layer group. */
private final DiagramViewer viewer;
/** The bottom layer. */ /** The bottom layer. */
private final Layer bottomLayer = new Layer(); private final Layer bottomLayer = new Layer();
/** The content node layer. */ /** The content node layer. */
...@@ -42,12 +39,9 @@ public final class DiagramLayers extends Group { ...@@ -42,12 +39,9 @@ public final class DiagramLayers extends Group {
private final Layer topLayer = new Layer(); private final Layer topLayer = new Layer();
/** The current mouse drag state associated with this bundle's visual. */ /** The current mouse drag state associated with this bundle's visual. */
private final MouseState mouseState; private final MouseState mouseState;
/** The scale to be applied for zooming the content of the layers. */
private Scale scale = new Scale();
/** Constructor. */ /** Constructor. */
public DiagramLayers(DiagramViewer viewer) { public DiagramLayers(DiagramViewer viewer) {
this.viewer = viewer;
ObservableList<Node> c = getChildren(); ObservableList<Node> c = getChildren();
c.add(bottomLayer); c.add(bottomLayer);
c.add(contentLayer); c.add(contentLayer);
...@@ -144,25 +138,6 @@ public final class DiagramLayers extends Group { ...@@ -144,25 +138,6 @@ public final class DiagramLayers extends Group {
topLayer.clear(); topLayer.clear();
} }
/** Registers the scroll listener and the zoom scale transformation. */
private void registerScrollListenerAndZoomScale(Node node) {
node.setOnScroll(viewer.getScrollingHandler());
node.getTransforms().add(scale);
}
/** Unregisters the scroll listener and the zoom scale transformation. */
private void unregisterScrollListenerAndZoomScale(Node node) {
node.setOnScroll(null);
node.getTransforms().remove(scale);
}
/** Scales all nodes in each layer. */
/* package */ void setScale(double factor) {
scale.setX(factor);
scale.setY(factor);
scale.setZ(factor);
}
/** Interface for layers. */ /** Interface for layers. */
public static interface ILayer { public static interface ILayer {
/** /**
...@@ -181,6 +156,7 @@ public final class DiagramLayers extends Group { ...@@ -181,6 +156,7 @@ public final class DiagramLayers extends Group {
/** Implementation of layers. */ /** Implementation of layers. */
private class Layer extends Group implements ILayer { private class Layer extends Group implements ILayer {
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public void add(Node node, IMVCBundle bundle) { public void add(Node node, IMVCBundle bundle) {
...@@ -188,7 +164,6 @@ public final class DiagramLayers extends Group { ...@@ -188,7 +164,6 @@ public final class DiagramLayers extends Group {
return; return;
} }
getChildren().add(node); getChildren().add(node);
registerScrollListenerAndZoomScale(node);
mouseState.registerMouseListeners(node, bundle); mouseState.registerMouseListeners(node, bundle);
} }
...@@ -199,7 +174,6 @@ public final class DiagramLayers extends Group { ...@@ -199,7 +174,6 @@ public final class DiagramLayers extends Group {
return; return;
} }
mouseState.unregisterMouseListeners(node); mouseState.unregisterMouseListeners(node);
unregisterScrollListenerAndZoomScale(node);
getChildren().remove(node); getChildren().remove(node);
} }
...@@ -208,7 +182,6 @@ public final class DiagramLayers extends Group { ...@@ -208,7 +182,6 @@ public final class DiagramLayers extends Group {
public void clear() { public void clear() {
for(Node node : getChildren()) { for(Node node : getChildren()) {
mouseState.unregisterMouseListeners(node); mouseState.unregisterMouseListeners(node);
unregisterScrollListenerAndZoomScale(node);
} }
getChildren().clear(); getChildren().clear();
} }
......
...@@ -10,10 +10,11 @@ ...@@ -10,10 +10,11 @@
package org.fortiss.tooling.common.ui.javafx.lwfxef; package org.fortiss.tooling.common.ui.javafx.lwfxef;
import static java.lang.Math.abs; import static java.lang.Math.abs;
import static java.lang.Math.ceil;
import static java.lang.Math.max; import static java.lang.Math.max;
import static java.lang.Math.min; import static java.lang.Math.min;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import static javafx.geometry.Orientation.HORIZONTAL;
import static javafx.geometry.Orientation.VERTICAL;
import static javafx.geometry.VPos.TOP; import static javafx.geometry.VPos.TOP;
import static javafx.scene.layout.GridPane.setHgrow; import static javafx.scene.layout.GridPane.setHgrow;
import static javafx.scene.layout.GridPane.setVgrow; import static javafx.scene.layout.GridPane.setVgrow;
...@@ -49,6 +50,7 @@ import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.MVCBundleTag; ...@@ -49,6 +50,7 @@ import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.MVCBundleTag;
import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IVisualFactory; import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IVisualFactory;
import javafx.event.EventHandler; import javafx.event.EventHandler;
import javafx.geometry.BoundingBox;
import javafx.geometry.Bounds; import javafx.geometry.Bounds;
import javafx.geometry.Orientation; import javafx.geometry.Orientation;
import javafx.geometry.Point2D; import javafx.geometry.Point2D;
...@@ -61,6 +63,8 @@ import javafx.scene.control.ScrollBar; ...@@ -61,6 +63,8 @@ import javafx.scene.control.ScrollBar;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.input.KeyEvent; import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent; import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.GridPane; import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane; import javafx.scene.layout.Pane;
...@@ -69,6 +73,9 @@ import javafx.scene.paint.Paint; ...@@ -69,6 +73,9 @@ import javafx.scene.paint.Paint;
import javafx.scene.shape.Line; import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle; import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text; import javafx.scene.text.Text;
import javafx.scene.transform.Affine;
import javafx.scene.transform.Transform;
import javafx.scene.transform.Translate;
/** /**
* This class represents the diagram viewer node, which manages the grid * This class represents the diagram viewer node, which manages the grid
...@@ -108,6 +115,8 @@ public class DiagramViewer { ...@@ -108,6 +115,8 @@ public class DiagramViewer {
private final Line linkLineFeedback = new Line(); private final Line linkLineFeedback = new Line();
/** The current context menu. */ /** The current context menu. */
private ContextMenu contextMenu = null; private ContextMenu contextMenu = null;
/** All scaling and translation of the diagram content is done through this transform. */
private Affine transform = new Affine();
/** A debug label. */ /** A debug label. */
private Label debugLabel = new Label(""); private Label debugLabel = new Label("");
...@@ -138,53 +147,42 @@ public class DiagramViewer { ...@@ -138,53 +147,42 @@ public class DiagramViewer {
// viewer pane // viewer pane
viewerPane = new Pane(); viewerPane = new Pane();
layers.boundsInLocalProperty().addListener((obs, ov, nv) -> { layers.boundsInLocalProperty().addListener((obs, ov, nv) -> {
updateHorizontalScrollbar(); updateScrollBars();
updateVerticalScrollbar();
}); });
layers.getTransforms().add(transform);
viewerPane.getChildren().add(layers); viewerPane.getChildren().add(layers);
viewerPane.widthProperty().addListener(evt -> { viewerPane.widthProperty().addListener(evt -> {
updateGridCanvasSize(); updateGridCanvasSize();
updateHorizontalScrollbar(); updateScrollBars();
}); });
viewerPane.heightProperty().addListener(evt -> { viewerPane.heightProperty().addListener(evt -> {
updateGridCanvasSize(); updateGridCanvasSize();
updateVerticalScrollbar(); updateScrollBars();
}); });
// focus event handling // focus event handling
viewerPane.focusedProperty().addListener((obs, ov, nv) -> { viewerPane.focusedProperty().addListener((obs, ov, nv) -> {
viewerManager.handleFocus(nv, getLayers()); viewerManager.handleFocus(nv, getLayers());
}); });
viewerPane.addEventFilter(MouseEvent.MOUSE_CLICKED, evt -> {
if(evt.isAltDown()) {
Point2D p = layers.sceneToLocal(evt.getSceneX(), evt.getSceneY());
scrollToCenter(new DiagramCoordinate(p.getX(), p.getY()));
}
});
// grid pane for viewer and scroll bars // grid pane for viewer and scroll bars
scrolledPane = new GridPane(); scrolledPane = new GridPane();
scrolledPane.add(viewerPane, 1, 1); scrolledPane.add(viewerPane, 1, 1);
setHgrow(viewerPane, ALWAYS); setHgrow(viewerPane, ALWAYS);
setVgrow(viewerPane, ALWAYS); setVgrow(viewerPane, ALWAYS);
// vertical scrollbar verticalScrollbar = createContentScrollBar(VERTICAL);
verticalScrollbar = new ScrollBar();
verticalScrollbar.setOrientation(Orientation.VERTICAL);
scrolledPane.add(verticalScrollbar, 2, 1); scrolledPane.add(verticalScrollbar, 2, 1);
verticalScrollbar.valueProperty().addListener((obs, ov, nv) -> {
hideContextMenu();
double zf = features.getCurrentZoomFactor();
double vszf = features.getVerticalSpacing() * zf;
double deltaY = nv.doubleValue() * vszf;
layers.setTranslateY(-deltaY);
viewerManager.gridCanvasVisual.setScrollY(nv.doubleValue());
});
// horizontal scrollbar horizontalScrollbar = createContentScrollBar(HORIZONTAL);
horizontalScrollbar = new ScrollBar();
scrolledPane.add(horizontalScrollbar, 1, 2); scrolledPane.add(horizontalScrollbar, 1, 2);
horizontalScrollbar.valueProperty().addListener((obs, ov, nv) -> {
hideContextMenu();
double zf = features.getCurrentZoomFactor();
double hszf = features.getHorizontalSpacing() * zf;
double deltaX = nv.doubleValue() * hszf;
layers.setTranslateX(-deltaX);
viewerManager.gridCanvasVisual.setScrollX(nv.doubleValue());
});
viewerPane.getChildren().add(debugLabel); viewerPane.getChildren().add(debugLabel);
debugLabel.setLayoutX(20); debugLabel.setLayoutX(20);
...@@ -213,6 +211,31 @@ public class DiagramViewer { ...@@ -213,6 +211,31 @@ public class DiagramViewer {
// update the viewer content // update the viewer content
viewerManager.updateContentObjects(); viewerManager.updateContentObjects();
// add event filters for scrolling and moving viewerPane
viewerPane.addEventFilter(ScrollEvent.SCROLL, getScrollingFilter());
ContentDragController contentDragController = new ContentDragController();
contentDragController.registerEventFilters(viewerPane);
}
/** Constructs a scrollbar controlling the content */
private ScrollBar createContentScrollBar(Orientation orientation) {
ScrollBar scrollbar = new ScrollBar();
scrollbar.setOrientation(orientation);
scrollbar.valueProperty().addListener((obs, ov, nv) -> {
hideContextMenu();
Point2D diagramTranslation = layers.parentToLocal(0, 0);
switch(orientation) {
case HORIZONTAL:
transform.appendTranslation(-nv.doubleValue() + diagramTranslation.getX(), 0);
break;
case VERTICAL:
transform.appendTranslation(0, -nv.doubleValue() + diagramTranslation.getY());
break;
}
viewerManager.gridCanvasVisual.updateNodes(layers);
});
return scrollbar;
} }
/** Returns viewerPane. */ /** Returns viewerPane. */
...@@ -242,19 +265,7 @@ public class DiagramViewer { ...@@ -242,19 +265,7 @@ public class DiagramViewer {
contextMenu = new ContextMenu(); contextMenu = new ContextMenu();
contextMenu.getItems().addAll(items); contextMenu.getItems().addAll(items);
contextMenu.setAutoHide(true); contextMenu.setAutoHide(true);
Point2D locationOnNode = diagramLocation.getLocal(node); Point2D screenLocation = layers.localToScreen(diagramLocation);
Point2D screenLocation;
double zf = features.getCurrentZoomFactor();
if(viewerManager.gridCanvasVisual.isGridCanvas(node)) {
double hsd = features.getHorizontalSpacing() * getHorizontalScrollBarValue() * zf;
double vsd = features.getVerticalSpacing() * getVerticalScrollBarValue() * zf;
Point2D correction = locationOnNode.multiply(zf).subtract(hsd, vsd);
screenLocation = node.localToScreen(correction);
} else {
Bounds b = node.getBoundsInLocal();
screenLocation = node.localToScreen(locationOnNode.getX() + b.getMinX(),
locationOnNode.getY() + b.getMinY());
}
contextMenu.show(node, screenLocation.getX(), screenLocation.getY()); contextMenu.show(node, screenLocation.getX(), screenLocation.getY());
} }
...@@ -278,45 +289,44 @@ public class DiagramViewer { ...@@ -278,45 +289,44 @@ public class DiagramViewer {
return exporter.export(); return exporter.export();
} }
/** Updates the vertical scrollbar. */ /** Updates the scrollbars. */
private void updateVerticalScrollbar() { private void updateScrollBars() {
Bounds vpb = viewerPane.getLayoutBounds(); // The size of the content (in diagram coordinates)
Bounds cb = layers.getLayoutBounds(); Bounds contentBounds = layers.getBoundsInLocal();
double vs = features.getVerticalSpacing(); double contentHeight = contentBounds.getHeight();
double contentHeight = max(ceil(cb.getMaxY() / vs), 2); double contentWidth = contentBounds.getWidth();
double viewportHeight = ceil(vpb.getHeight() / vs); // The size of the visible content (in diagram coordinates)
double zf = features.getCurrentZoomFactor(); Bounds visibleBounds = layers.parentToLocal(viewerPane.getLayoutBounds());
// this allows scrolling the content nearly out of sight (two grid spaces) double visibleWidth = visibleBounds.getWidth();
double max = contentHeight - 2 * zf; double visibleHeight = visibleBounds.getHeight();
verticalScrollbar.setMax(max);
double visi = max * (viewportHeight / (viewportHeight + contentHeight - 2)); verticalScrollbar.setMin(0);
verticalScrollbar.setVisibleAmount(visi); horizontalScrollbar.setMin(0);
verticalScrollbar.setMax(contentHeight);
horizontalScrollbar.setMax(contentWidth);
Point2D diagramTopLeft = layers.parentToLocal(0, 0);
verticalScrollbar.setValue(diagramTopLeft.getY());
horizontalScrollbar.setValue(diagramTopLeft.getX());
// Including blank space to the bottom, the height of the content
// is (contentHeight + visibleHeight), of which visibleHeight is
// visible. Since the vertical scrollbar goes from 0 to contentHeight,
// this means we need:
// contentHeight / verticalVisibleAmount
// == (contentHeight + visibleHeight) / visibleHeight
// Hence:
double verticalVisibleAmount =
contentHeight * visibleHeight / (contentHeight + visibleHeight);
double horizontalVisibleAmount =
contentWidth * visibleWidth / (contentWidth + visibleWidth);
verticalScrollbar.setVisibleAmount(verticalVisibleAmount);
horizontalScrollbar.setVisibleAmount(horizontalVisibleAmount);
verticalScrollbar.setUnitIncrement(1); verticalScrollbar.setUnitIncrement(1);
verticalScrollbar.setBlockIncrement(5);
if(verticalScrollbar.getValue() > max) {
verticalScrollbar.setValue(max);
}
}
/** Updates the horizontal scrollbar. */
private void updateHorizontalScrollbar() {
Bounds vpb = viewerPane.getLayoutBounds();
Bounds cb = layers.getLayoutBounds();
double hs = features.getHorizontalSpacing();
double contentWidth = max(ceil(cb.getMaxX() / hs), 2);
double viewportWidth = ceil(vpb.getWidth() / hs);
double zf = features.getCurrentZoomFactor();
// this allows scrolling the content nearly out of sight (two grid spaces)
double max = contentWidth - 2 * zf;
horizontalScrollbar.setMax(max);
double visi = max * (viewportWidth / (viewportWidth + contentWidth - 2));
horizontalScrollbar.setVisibleAmount(visi);
horizontalScrollbar.setUnitIncrement(1); horizontalScrollbar.setUnitIncrement(1);
horizontalScrollbar.setBlockIncrement(5); verticalScrollbar.setBlockIncrement(verticalScrollbar.getVisibleAmount());
if(horizontalScrollbar.getValue() > max) { horizontalScrollbar.setBlockIncrement(horizontalScrollbar.getVisibleAmount());
horizontalScrollbar.setValue(max);
}
} }
/** Update the size of the grid canvas. */ /** Update the size of the grid canvas. */
...@@ -429,33 +439,71 @@ public class DiagramViewer { ...@@ -429,33 +439,71 @@ public class DiagramViewer {
return viewerManager.modelFactory; return viewerManager.modelFactory;
} }
/** Clamp content so that it can't be moved beyond the borders. */
private void enforceBounds() {
// The size of the content (in diagram coordinates)
Bounds contentBounds = layers.getBoundsInLocal();
// Crop at the top left corner
BoundingBox enforceContentBounds =
new BoundingBox(0, 0, contentBounds.getMaxX(), contentBounds.getMaxY());
Bounds contentInParent = layers.localToParent(enforceContentBounds);
double leftBoundCorrection = max(contentInParent.getMinX(), 0);
double topBoundCorrection = max(contentInParent.getMinY(), 0);
double rightBoundCorrection = min(contentInParent.getMaxX(), 0);
double bottomBoundCorrection = min(contentInParent.getMaxY(), 0);
Point2D clampedInParent = new Point2D(leftBoundCorrection + rightBoundCorrection,
topBoundCorrection + bottomBoundCorrection);
Point2D correctionLocal = layers.parentToLocal(clampedInParent);
Point2D originLocal = layers.parentToLocal(0, 0);
transform.appendTranslation(originLocal.getX() - correctionLocal.getX(),
originLocal.getY() - correctionLocal.getY());
}
/** Apply a transformation to the content. */
void appendContentTransform(Transform t) {
transform.append(t);
enforceBounds();
updateScrollBars();
viewerManager.gridCanvasVisual.updateNodes(layers);
}
/** Returns the scroll event handler. */ /** Returns the scroll event handler. */
/* package */ EventHandler<? super ScrollEvent> getScrollingHandler() { private EventHandler<? super ScrollEvent> getScrollingFilter() {
return evt -> { return evt -> {
evt.consume();
if(evt.isControlDown()) { if(evt.isControlDown()) {
if(features.getZoomFactorIndex() == -1) { if(features.getZoomFactorIndex() == -1) {
evt.consume();
return; return;
} }
if(evt.getDeltaY() > 0) { if(evt.getDeltaY() > 0) {
features.zoomIn(); Point2D pivot = layers.sceneToLocal(evt.getSceneX(), evt.getSceneY());
evt.consume(); features.zoomIn(pivot.getX(), pivot.getY());
} else if(evt.getDeltaY() < 0) { } else if(evt.getDeltaY() < 0) {
features.zoomOut(); Point2D pivot = layers.sceneToLocal(evt.getSceneX(), evt.getSceneY());
evt.consume(); features.zoomOut(pivot.getX(), pivot.getY());
} }
} else if(evt.isShiftDown()) { } else if(evt.isShiftDown()) {
Point2D p0 = layers.screenToLocal(0, 0);
Point2D p1 = layers.screenToLocal(1, 1);
double hmax = horizontalScrollbar.getMax(); double hmax = horizontalScrollbar.getMax();
double hmin = horizontalScrollbar.getMin(); double hmin = horizontalScrollbar.getMin();
double nval = horizontalScrollbar.getValue() - double nval = horizontalScrollbar.getValue() -
evt.getDeltaY() / features.getHorizontalSpacing(); evt.getDeltaY() * abs(p1.getX() - p0.getX());
horizontalScrollbar.setValue(max(hmin, min(hmax, nval))); horizontalScrollbar.setValue(max(hmin, min(hmax, nval)));
} else { } else {
Point2D p0 = layers.screenToLocal(0, 0);
Point2D p1 = layers.screenToLocal(1, 1);
double vmax = verticalScrollbar.getMax(); double vmax = verticalScrollbar.getMax();
double vmin = verticalScrollbar.getMin(); double vmin = verticalScrollbar.getMin();
double nval = verticalScrollbar.getValue() - double vval =
evt.getDeltaY() / features.getVerticalSpacing(); verticalScrollbar.getValue() - evt.getDeltaY() * abs(p1.getY() - p0.getY());
verticalScrollbar.setValue(max(vmin, min(vmax, nval))); double hmax = horizontalScrollbar.getMax();
double hmin = horizontalScrollbar.getMin();
double hval = horizontalScrollbar.getValue() -
evt.getDeltaX() * abs(p1.getX() - p0.getX());
verticalScrollbar.setValue(max(vmin, min(vmax, vval)));
horizontalScrollbar.setValue(max(hmin, min(hmax, hval)));
} }
}; };
} }
...@@ -486,11 +534,7 @@ public class DiagramViewer { ...@@ -486,11 +534,7 @@ public class DiagramViewer {
* using the underlying model. * using the underlying model.
*/ */
public void updateFromModel() { public void updateFromModel() {
double vscroll = verticalScrollbar.getValue();
double hscroll = horizontalScrollbar.getValue();
viewerManager.updateContentObjects(); viewerManager.updateContentObjects();
verticalScrollbar.setValue(vscroll);
horizontalScrollbar.setValue(hscroll);
} }
/** /**
...@@ -533,9 +577,7 @@ public class DiagramViewer { ...@@ -533,9 +577,7 @@ public class DiagramViewer {
setSingleSelectedMVCBundle(null); setSingleSelectedMVCBundle(null);
} }
/** /** Converts the given absolute diagram coordinates to node-local coordinates. */
* Converts the given absolute diagram coordinates to node-local coordinates.
*/
/* package */ DiagramCoordinate convertEventToDiagramCoordinates(Point2D absoluteLocation, /* package */ DiagramCoordinate convertEventToDiagramCoordinates(Point2D absoluteLocation,
Node node) { Node node) {
return convertEventToDiagramCoordinates(absoluteLocation.getX(), absoluteLocation.getY(), return convertEventToDiagramCoordinates(absoluteLocation.getX(), absoluteLocation.getY(),
...@@ -545,37 +587,25 @@ public class DiagramViewer { ...@@ -545,37 +587,25 @@ public class DiagramViewer {
/** Converts the given mouse event coordinates to node-local coordinates. */ /** Converts the given mouse event coordinates to node-local coordinates. */
/* package */ DiagramCoordinate convertEventToDiagramCoordinates(double evtX, double evtY, /* package */ DiagramCoordinate convertEventToDiagramCoordinates(double evtX, double evtY,
Node node) { Node node) {
if(node == viewerManager.gridCanvasVisual.getGridCanvas()) { Point2D scene = node.localToScene(evtX, evtY);
return convertGridCanvasCoordinate(evtX, evtY); Point2D diagram = layers.sceneToLocal(scene);
} return new DiagramCoordinate(diagram.getX(), diagram.getY());
return new DiagramCoordinate(evtX, evtY);
} }
/** Converts the grid canvas coordinate to the diagram coordinate space. */ /** By default, zoom fixes the top left corner */
public DiagramCoordinate convertGridCanvasCoordinate(double xOnCanvas, double yOnCanvas) { /* package */ DiagramCoordinate getDefaultZoomPivot() {
double zf = features.getCurrentZoomFactor(); Point2D pivot = layers.parentToLocal(0, 0);
double x = xOnCanvas / zf + features.getHorizontalSpacing() * getHorizontalScrollBarValue(); return new DiagramCoordinate(pivot.getX(), pivot.getY());
double y = yOnCanvas / zf + features.getVerticalSpacing() * getVerticalScrollBarValue();
return new DiagramCoordinate(x, y);
} }
/** Scrolls the diagram to center at the given coordinates. */ /** Scrolls the diagram to center at the given coordinates. */
public void scrollToCenter(DiagramCoordinate center) { public void scrollToCenter(DiagramCoordinate center) {
double zf = features.getCurrentZoomFactor(); Point2D centerScene =
Bounds bounds = viewerPane.getLayoutBounds(); viewerPane.localToScene(viewerPane.getWidth() / 2, viewerPane.getHeight() / 2);
double upperLeftX = center.getX() - bounds.getMinX() / zf; Point2D currentCenter = layers.sceneToLocal(centerScene);
if(upperLeftX < 0) { double dx = currentCenter.getX() - center.getX();
upperLeftX = 0; double dy = currentCenter.getY() - center.getY();
} appendContentTransform(new Translate(dx, dy));
double newHSBValue = upperLeftX / features.getHorizontalSpacing();
double upperLeftY = center.getY() - bounds.getMinY() / zf;
if(upperLeftY < 0) {
upperLeftY = 0;
}
double newVSBValue = upperLeftY - features.getVerticalSpacing();
horizontalScrollbar.setValue(newHSBValue);
verticalScrollbar.setValue(newVSBValue);
} }
/** Updates the feedback for selection rectangle. */ /** Updates the feedback for selection rectangle. */
...@@ -622,10 +652,7 @@ public class DiagramViewer { ...@@ -622,10 +652,7 @@ public class DiagramViewer {
} }
} }
/** /** Starts the feedback of the link creation line and shows the possible target feedbacks. */
* Starts the feedback of the link creation line and shows the possible target
* feedbacks.
*/
public void startNewLinkLineFeedback(IMVCBundle linkStartBundle, Node node, public void startNewLinkLineFeedback(IMVCBundle linkStartBundle, Node node,
Point2D locationInDiagram) { Point2D locationInDiagram) {
if(node == null || locationInDiagram == null) { if(node == null || locationInDiagram == null) {
...@@ -662,7 +689,7 @@ public class DiagramViewer { ...@@ -662,7 +689,7 @@ public class DiagramViewer {
clearLinkTargetFeedback(); clearLinkTargetFeedback();
} }
/** Clears the link target feedback fronm all possible link targets. */ /** Clears the link target feedback from all possible link targets. */
private void clearLinkTargetFeedback() { private void clearLinkTargetFeedback() {
for(IMVCBundle possibleTarget : viewerManager.possibleLinkTargets) { for(IMVCBundle possibleTarget : viewerManager.possibleLinkTargets) {
possibleTarget.removeTag(LINK_TARGET_ALLOWED_TAG); possibleTarget.removeTag(LINK_TARGET_ALLOWED_TAG);
...@@ -857,4 +884,64 @@ public class DiagramViewer { ...@@ -857,4 +884,64 @@ public class DiagramViewer {
return null; return null;
} }
} }
/** Mouse controller for moving the content using control-drag. */
private final class ContentDragController {
/** Last point within a control-drag gesture. */
private Point2D dragPoint;
/**
* Has the mouse button been released within a control-drag gesture? If so, the next click
* event should be filtered out.
*/
private boolean buttonReleased;
/** Register the event filters of this controller. */
/* package */ void registerEventFilters(Node node) {
node.addEventFilter(MouseEvent.MOUSE_DRAGGED, dragDetected);
node.addEventFilter(MouseEvent.ANY, dragged);
node.addEventFilter(MouseEvent.MOUSE_RELEASED, mouseReleased);
node.addEventFilter(MouseEvent.MOUSE_CLICKED, mouseClicked);
}
/** Control-drag gesture starts when drag is detected. */
private final EventHandler<MouseEvent> dragDetected = evt -> {
if(dragPoint == null && evt.isControlDown() && evt.getButton() == MouseButton.PRIMARY) {
dragPoint = getLayers().sceneToLocal(evt.getSceneX(), evt.getSceneY());
buttonReleased = false;
evt.consume();
}
};
/** While dragging, the content is translated appropriately. */
private final EventHandler<MouseEvent> dragged = evt -> {
if(dragPoint != null) {
evt.consume();
Point2D evtLayer = getLayers().sceneToLocal(evt.getSceneX(), evt.getSceneY());
Point2D move = evtLayer.subtract(dragPoint);
double dx = move.getX();
double dy = move.getY();
appendContentTransform(new Translate(dx, dy));
dragPoint = getLayers().sceneToLocal(evt.getSceneX(), evt.getSceneY());
}
};
/** If the mouse button is released, the gesture should be stopped. */
private final EventHandler<MouseEvent> mouseReleased = evt -> {
if(dragPoint != null && evt.getButton() == MouseButton.PRIMARY) {
dragPoint = null;
buttonReleased = true;
evt.consume();
}
};
/** The first click event after the released event should be filtered. */
private final EventHandler<MouseEvent> mouseClicked = evt -> {
if(buttonReleased && evt.getButton() == MouseButton.PRIMARY) {
evt.consume();
}
buttonReleased = false;
};
}
} }
...@@ -14,9 +14,12 @@ import static java.lang.Math.max; ...@@ -14,9 +14,12 @@ import static java.lang.Math.max;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import static javafx.scene.paint.Color.LIGHTGRAY; import static javafx.scene.paint.Color.LIGHTGRAY;
import java.util.Objects;
import javafx.geometry.Dimension2D; import javafx.geometry.Dimension2D;
import javafx.geometry.Point2D; import javafx.geometry.Point2D;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import javafx.scene.transform.Scale;
/** /**
* This class handles the state of all features of the {@link DiagramViewer}, which can be toggled * This class handles the state of all features of the {@link DiagramViewer}, which can be toggled
...@@ -25,10 +28,14 @@ import javafx.scene.paint.Color; ...@@ -25,10 +28,14 @@ import javafx.scene.paint.Color;
public final class DiagramViewerFeatures { public final class DiagramViewerFeatures {
/** The diagram viewer. */ /** The diagram viewer. */
private final DiagramViewer viewer; private final DiagramViewer viewer;
/** The zoom factors. */ /**
private double[] zoomFactors = new double[] {0.125, 0.25, 0.5, 0.75, 1, 1.5, 2, 4, 8}; * The zoom factors. The values are chosen to have a roughly constant factor between them, to
* make interactive scrolling less jumpy.
*/
private double[] zoomFactors = new double[] {0.125, 0.25, 0.3, 0.4, 0.5, 0.65, 0.80, 1, 1.25,
1.5, 2.0, 2.5, 3.5, 4.5, 6};
/** The current zoom factor array index. */ /** The current zoom factor array index. */
private int zoomFactorIndex = 4; // Index of 1 private int zoomFactorIndex = 7; // Index of 1
/** Flag indicating whether link highlighting is enabled. */ /** Flag indicating whether link highlighting is enabled. */
private boolean linkHighlightingEnabled = false; private boolean linkHighlightingEnabled = false;
/** Flag if interaction area shading is active. */ /** Flag if interaction area shading is active. */
...@@ -81,23 +88,40 @@ public final class DiagramViewerFeatures { ...@@ -81,23 +88,40 @@ public final class DiagramViewerFeatures {
/** Sets the zoom factor index. */ /** Sets the zoom factor index. */
public void setZoomFactorIndex(int zoomFactorIndex) { public void setZoomFactorIndex(int zoomFactorIndex) {
Objects.checkIndex(zoomFactorIndex, zoomFactors.length);
double factor = zoomFactors[zoomFactorIndex] / zoomFactors[this.zoomFactorIndex];
this.zoomFactorIndex = zoomFactorIndex; this.zoomFactorIndex = zoomFactorIndex;
viewer.updateAllVisuals(); DiagramCoordinate pivot = viewer.getDefaultZoomPivot();
viewer.appendContentTransform(new Scale(factor, factor, pivot.getX(), pivot.getY()));
} }
/** Increases zoom factor index by one up to the maximum. */ /** Increases zoom factor index by one up to the maximum. */
public void zoomIn() { public void zoomIn() {
DiagramCoordinate pivot = viewer.getDefaultZoomPivot();
zoomIn(pivot.getX(), pivot.getY());
}
/** Increases zoom factor index by one up to the maximum. */
public void zoomIn(double pivotX, double pivotY) {
if(zoomFactorIndex < zoomFactors.length - 1) { if(zoomFactorIndex < zoomFactors.length - 1) {
zoomFactorIndex++; zoomFactorIndex++;
viewer.updateAllVisuals(); double factor = zoomFactors[zoomFactorIndex] / zoomFactors[zoomFactorIndex - 1];
viewer.appendContentTransform(new Scale(factor, factor, pivotX, pivotY));
} }
} }
/** Decreases zoom factor index by one down to the minimum. */ /** Decreases zoom factor index by one down to the minimum. */
public void zoomOut() { public void zoomOut() {
DiagramCoordinate pivot = viewer.getDefaultZoomPivot();
zoomOut(pivot.getX(), pivot.getY());
}
/** Decreases zoom factor index by one down to the minimum. */
public void zoomOut(double pivotX, double pivotY) {
if(zoomFactorIndex > 0) { if(zoomFactorIndex > 0) {
zoomFactorIndex--; zoomFactorIndex--;
viewer.updateAllVisuals(); double factor = zoomFactors[zoomFactorIndex] / zoomFactors[zoomFactorIndex + 1];
viewer.appendContentTransform(new Scale(factor, factor, pivotX, pivotY));
} }
} }
......
...@@ -9,6 +9,10 @@ ...@@ -9,6 +9,10 @@
*******************************************************************************/ *******************************************************************************/
package org.fortiss.tooling.common.ui.javafx.lwfxef; package org.fortiss.tooling.common.ui.javafx.lwfxef;
import static java.lang.Double.doubleToLongBits;
import java.util.Objects;
import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerFeatures.IndicatorType; import org.fortiss.tooling.common.ui.javafx.lwfxef.DiagramViewerFeatures.IndicatorType;
import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundle; import org.fortiss.tooling.common.ui.javafx.lwfxef.mvc.IMVCBundle;
import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IVisual; import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IVisual;
...@@ -19,7 +23,9 @@ import javafx.geometry.Rectangle2D; ...@@ -19,7 +23,9 @@ import javafx.geometry.Rectangle2D;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.canvas.Canvas; import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext; import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
/** /**
* {@link IVisual} for the diagram background. Note that the {@link Canvas} used here is always as * {@link IVisual} for the diagram background. Note that the {@link Canvas} used here is always as
...@@ -30,27 +36,46 @@ import javafx.scene.paint.Color; ...@@ -30,27 +36,46 @@ import javafx.scene.paint.Color;
final class GridCanvasVisual implements IVisual { final class GridCanvasVisual implements IVisual {
/** The diagram bundle. */ /** The diagram bundle. */
private final DiagramViewer viewer; private final DiagramViewer viewer;
/** The FX canvas to be drawn on. */
/** The pane to contain the whole grid. */
private final Pane pane;
/**
* The FX canvas to draw the grid. The canvas is one grid column/row larger than the pane. It
* gets moved on the pane when the position of the grid changes. In this way, the canvas only
* needs to be redrawn when scale or grid configuration change. Moving the final image is much
* faster than redrawing the canvas.
*/
private final Canvas gridCanvas; private final Canvas gridCanvas;
/** The displacement caused by scroll bars. */
private DiagramCoordinate displacement = new DiagramCoordinate(0, 0); /**
* A rectangle for the left outer border. The border is not painted with the canvas to avoid
* having to redraw the canvas when changing location.
*/
private final Rectangle leftOuterBorder;
/** A rectangle for the top outer border. */
private final Rectangle topOuterBorder;
/** Width of the whole shown grid. */
double width;
/** Height of the whole shown grid. */
double height;
/** Constructor. */ /** Constructor. */
public GridCanvasVisual(DiagramViewer viewer) { public GridCanvasVisual(DiagramViewer viewer) {
this.viewer = viewer; this.viewer = viewer;
// the background grid canvas this.pane = new Pane();
this.gridCanvas = new Canvas(100, 100); this.gridCanvas = new Canvas();
gridCanvas.widthProperty().addListener(evt -> { this.leftOuterBorder = new Rectangle();
paintGrid(); this.topOuterBorder = new Rectangle();
}); this.pane.getChildren().addAll(gridCanvas, leftOuterBorder, topOuterBorder);
gridCanvas.heightProperty().addListener(evt -> {
paintGrid();
});
} }
/** Returns gridCanvas. */ /** Returns gridCanvas. */
/* package */Canvas getGridCanvas() { /* package */Node getGridNode() {
return gridCanvas; return pane;
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
...@@ -74,27 +99,25 @@ final class GridCanvasVisual implements IVisual { ...@@ -74,27 +99,25 @@ final class GridCanvasVisual implements IVisual {
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public void updateNodes(DiagramLayers layers) { public void updateNodes(DiagramLayers layers) {
if(gridCanvas.getParent() == null) { if(pane.getParent() == null) {
// grid canvas is added below the diagram layers // grid is added below the diagram layers
viewer.getViewerPane().getChildren().add(0, gridCanvas); viewer.getViewerPane().getChildren().add(0, pane);
viewer.getLayers().getMouseState().registerMouseListeners(gridCanvas, getMVCBundle()); viewer.getLayers().getMouseState().registerMouseListeners(pane, getMVCBundle());
gridCanvas.setOnScroll(viewer.getScrollingHandler());
} }
paintGrid(); updateContent();
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public void removeAllVisuals(DiagramLayers layers) { public void removeAllVisuals(DiagramLayers layers) {
viewer.getLayers().getMouseState().unregisterMouseListeners(gridCanvas); viewer.getLayers().getMouseState().unregisterMouseListeners(pane);
viewer.getViewerPane().getChildren().remove(gridCanvas); viewer.getViewerPane().getChildren().remove(pane);
gridCanvas.setOnScroll(null);
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public Rectangle2D getModelBounds() { public Rectangle2D getModelBounds() {
Bounds b = gridCanvas.getBoundsInParent(); Bounds b = pane.getBoundsInParent();
return new Rectangle2D(b.getMinX(), b.getMinY(), b.getWidth(), b.getHeight()); return new Rectangle2D(b.getMinX(), b.getMinY(), b.getWidth(), b.getHeight());
} }
...@@ -115,49 +138,189 @@ final class GridCanvasVisual implements IVisual { ...@@ -115,49 +138,189 @@ final class GridCanvasVisual implements IVisual {
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public void requestFocus() { public void requestFocus() {
gridCanvas.requestFocus(); pane.requestFocus();
}
/** Returns the zoom factor from current content transformation. */
private double getZoomFactor() {
Point2D unitVector = new Point2D(1, 1);
Point2D transformedVector =
viewer.getLayers().getLocalToParentTransform().deltaTransform(unitVector);
return transformedVector.getX();
}
/** Structure to represent the current location of the grid. */
private static class GridOffset {
/** Grid x coordinate of upper left corner. */
int gridX;
/** Grid y coordinate of upper left corner. */
int gridY;
/** Insets in the grid square between (gridX, gridY) and (gridX + 1, gridY +1). */
Point2D insets;
} }
/** Paints the grid. */ /** Compute the grid offset current content transformation. */
public void paintGrid() { private GridOffset getGridOffset() {
DiagramViewerFeatures features = viewer.getFeatures(); DiagramViewerFeatures features = viewer.getFeatures();
double zf = features.getCurrentZoomFactor(); Point2D p0 = viewer.getLayers().sceneToLocal(pane.localToScene(0, 0));
double width = gridCanvas.getWidth();
double height = gridCanvas.getHeight(); GridOffset gridOffset = new GridOffset();
GraphicsContext gc = gridCanvas.getGraphicsContext2D(); gridOffset.gridX = (int)(p0.getX() / features.getHorizontalSpacing());
// background gridOffset.gridY = (int)(p0.getY() / features.getVerticalSpacing());
Color backgroundColor = features.getBackgroundColor(); gridOffset.insets = new Point2D((p0.getX() / features.getHorizontalSpacing()) % 1.0,
gc.setFill(backgroundColor); (p0.getY() / features.getVerticalSpacing()) % 1.0);
gc.setStroke(backgroundColor); return gridOffset;
gc.fillRect(0, 0, width, height); }
// outer border and indicators
Color indicatorColor = features.getIndicatorColor(); /**
gc.setStroke(indicatorColor); * Structure representing the parameters that go into the drawing of the grid canvas. It is
gc.setFill(indicatorColor); * useful to determine if the canvas needs repainting.
double hSpacing = features.getHorizontalSpacing(); */
double vSpacing = features.getVerticalSpacing(); private static class GridParameters {
double dx = displacement.getX() % 1.0;
double dy = displacement.getY() % 1.0; /** Horizontal grid spacing. */
double zfhs = zf * hSpacing; private double hSpacing;
double zfvs = zf * vSpacing;
/** Vertical grid spacing. */
private double vSpacing;
/** Zoom factor. */
private double zoomFactor;
/** Grid indicator size. */
private double indicatorSize;
/** Grid indicator type. */
private IndicatorType indicatorType;
/** Grid background color. */
private Color backgroundColor;
/** Grid indicator color. */
private Color indicatorColor;
/** {@inheritDoc} */
@Override
public int hashCode() {
return Objects.hash(backgroundColor, hSpacing, indicatorColor, indicatorSize,
indicatorType, vSpacing, zoomFactor);
}
/**
* Auto-generated (by Eclipse) deep equality method.
*
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj) {
if(this == obj) {
return true;
}
if(obj == null) {
return false;
}
if(getClass() != obj.getClass()) {
return false;
}
GridParameters other = (GridParameters)obj;
// Compare all member variables individually for equality.
// The equality of doubles is tested using {@code doubleToLongBits},
// as explained in {@link java.lang.Double#equals(Object)}.
return Objects.equals(backgroundColor, other.backgroundColor) &&
doubleToLongBits(hSpacing) == doubleToLongBits(other.hSpacing) &&
Objects.equals(indicatorColor, other.indicatorColor) &&
doubleToLongBits(indicatorSize) == doubleToLongBits(other.indicatorSize) &&
indicatorType == other.indicatorType &&
doubleToLongBits(vSpacing) == doubleToLongBits(other.vSpacing) &&
doubleToLongBits(zoomFactor) == doubleToLongBits(other.zoomFactor);
}
}
/** Update the pane node. */
private void updateContent() {
DiagramViewerFeatures features = viewer.getFeatures();
GridParameters parameters = new GridParameters();
parameters.hSpacing = features.getHorizontalSpacing();
parameters.vSpacing = features.getVerticalSpacing();
parameters.zoomFactor = getZoomFactor();
parameters.indicatorType = features.getIndicatorType();
parameters.backgroundColor = features.getBackgroundColor();
parameters.indicatorColor = features.getIndicatorColor();
parameters.indicatorSize = features.getIndicatorSize();
paintCanvas(parameters);
GridOffset gridOffset = getGridOffset();
double zfhs = parameters.zoomFactor * parameters.hSpacing;
double zfvs = parameters.zoomFactor * parameters.vSpacing;
gridCanvas.relocate(-gridOffset.insets.getX() * zfhs, -gridOffset.insets.getY() * zfvs);
pane.getChildren().removeAll(leftOuterBorder, topOuterBorder);
if(features.isDrawOuterBorder()) { if(features.isDrawOuterBorder()) {
if(displacement.getX() < 1.0) { pane.getChildren().addAll(leftOuterBorder, topOuterBorder);
gc.fillRect(0, 0, zfhs * (1.0 - dx), height); leftOuterBorder.relocate(-(gridOffset.gridX + gridOffset.insets.getX()) * zfhs, 0);
leftOuterBorder.setWidth(zfhs);
leftOuterBorder.setHeight(height);
leftOuterBorder.setStrokeWidth(0.0);
leftOuterBorder.setFill(features.getIndicatorColor());
topOuterBorder.relocate(0, -(gridOffset.gridY + gridOffset.insets.getY()) * zfhs);
topOuterBorder.setWidth(width);
topOuterBorder.setHeight(zfvs);
topOuterBorder.setStrokeWidth(0.0);
topOuterBorder.setFill(features.getIndicatorColor());
} }
if(displacement.getY() < 1.0) {
gc.fillRect(0, 0, width, zfvs * (1.0 - dy));
} }
/** The parameters of what is currently drawn on the gridCanvas. */
private GridParameters currentGridCanvasParameters = null;
/** Draw gridCanvas with the given parameters. */
private void paintCanvas(GridParameters parameters) {
double targetCanvasWidth = width + parameters.zoomFactor * parameters.hSpacing;
double targetCanvasHeight = height + parameters.zoomFactor * parameters.vSpacing;
boolean resized = false;
resized |= gridCanvas.getWidth() != targetCanvasWidth;
resized |= gridCanvas.getHeight() != targetCanvasHeight;
if(resized) {
gridCanvas.setWidth(targetCanvasWidth);
gridCanvas.setHeight(targetCanvasHeight);
}
if(!resized && parameters.equals(currentGridCanvasParameters)) {
// grid is already drawn on canvas
return;
} }
currentGridCanvasParameters = parameters;
GraphicsContext gc = gridCanvas.getGraphicsContext2D();
// background
gc.setFill(parameters.backgroundColor);
gc.setStroke(parameters.backgroundColor);
gc.fillRect(0, 0, gridCanvas.getWidth(), gridCanvas.getHeight());
// abort if no indicators should be drawn // abort if no indicators should be drawn
IndicatorType indicatorType = features.getIndicatorType(); IndicatorType indicatorType = parameters.indicatorType;
if(indicatorType == IndicatorType.INVISIBLE) { if(indicatorType == IndicatorType.INVISIBLE) {
return; return;
} }
// draw grid indicators // draw grid indicators
double isize = viewer.getFeatures().getIndicatorSize() * zf; gc.setStroke(parameters.indicatorColor);
for(double x = zfhs * (1.0 - dx); x < width; x += zfhs) { gc.setFill(parameters.indicatorColor);
for(double y = zfvs * (1.0 - dy); y < height; y += zfvs) { double zfhs = parameters.zoomFactor * parameters.hSpacing;
drawIndicator(gc, x, y, isize, indicatorType); double zfvs = parameters.zoomFactor * parameters.vSpacing;
double isize = parameters.indicatorSize * parameters.zoomFactor;
for(double x = zfhs; x < gridCanvas.getWidth(); x += zfhs) {
for(double y = zfvs; y < gridCanvas.getHeight(); y += zfvs) {
drawIndicator(gc, Math.floor(x) + 0.5, Math.floor(y) + 0.5, isize, indicatorType);
} }
} }
} }
...@@ -199,41 +362,21 @@ final class GridCanvasVisual implements IVisual { ...@@ -199,41 +362,21 @@ final class GridCanvasVisual implements IVisual {
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public EDragGesture getDragGesture(Node node, DiagramCoordinate diagramLocation) { public EDragGesture getDragGesture(Node node, DiagramCoordinate diagramLocation) {
if(node == gridCanvas) { if(node == pane) {
return EDragGesture.SELECTION; return EDragGesture.SELECTION;
} }
return EDragGesture.NONE; return EDragGesture.NONE;
} }
/** Sets horizontal scroll displacement. */
public void setScrollX(double canvasDeltaX) {
displacement = new DiagramCoordinate(canvasDeltaX, displacement.getY());
paintGrid();
}
/** Sets vertical scroll displacement. */
public void setScrollY(double canvasDeltaY) {
displacement = new DiagramCoordinate(displacement.getX(), canvasDeltaY);
paintGrid();
}
/** Sets the width of the grid canvas. */ /** Sets the width of the grid canvas. */
public void setWidth(double w) { public void setWidth(double w) {
gridCanvas.setWidth(w); this.width = w;
updateContent();
} }
/** Sets the height of the grid canvas. */ /** Sets the height of the grid canvas. */
public void setHeight(double h) { public void setHeight(double h) {
gridCanvas.setHeight(h); this.height = h;
} updateContent();
/** Relocates the given scene point to local coordinates. */
public Point2D sceneToLocal(Point2D locationInScene) {
return gridCanvas.sceneToLocal(locationInScene);
}
/** Returns whether the given node is the grid canvas. */
public boolean isGridCanvas(Node source) {
return source == gridCanvas;
} }
} }
...@@ -502,7 +502,6 @@ import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IVisualFactory; ...@@ -502,7 +502,6 @@ import org.fortiss.tooling.common.ui.javafx.lwfxef.visual.IVisualFactory;
/** Updates every visual (and only its appearance, not the structure). */ /** Updates every visual (and only its appearance, not the structure). */
/* package */ void updateAllVisuals() { /* package */ void updateAllVisuals() {
layers.setScale(viewer.getFeatures().getCurrentZoomFactor());
viewer.updateGridCanvasSize(); viewer.updateGridCanvasSize();
gridCanvasVisual.updateNodes(layers); gridCanvasVisual.updateNodes(layers);
for(IContentMVCBundle b : contentBundles.values()) { for(IContentMVCBundle b : contentBundles.values()) {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment